Anakunda / Qobuz Discovery

// ==UserScript==
// @name         Qobuz Discovery
// @namespace    https://openuserjs.org/users/Anakunda
// @run-at       document-end
// @version      2.05.8
// @description  Find easily new stuff on Qobuz
// @author       Anakunda
// @copyright    2020-21, Anakunda (https://openuserjs.org/users/Anakunda)
// @license      GPL-3.0-or-later
// @iconURL      https://static-www.qobuz.com/img/favicon/favicon-96x96.png
// @match        https://www.qobuz.com/*/download-streaming-albums*
// @match        https://www.qobuz.com/download-streaming-albums*
// @match        https://qobuz.com/*/download-streaming-albums*
// @match        https://qobuz.com/download-streaming-albums*
// @match        https://www.qobuz.com/*/search?*
// @match        https://www.qobuz.com/search?*
// @match        https://qobuz.com/*/search?*
// @match        https://qobuz.com/search?*
// @connect      redacted.ch
// @connect      orpheus.network
// @connect      notwhat.cd
// @connect      api.deezer.com
// @connect      www.xe.com
// @connect      www.x-rates.com
// @connect      play.qobuz.com
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_registerMenuCommand
// @grant        GM_info
// @grant        GM_openInTab
// @require      https://openuserjs.org/src/libs/Anakunda/xhrLib.min.js
// @require      https://openuserjs.org/src/libs/Anakunda/libStringDistance.min.js
// @require      https://openuserjs.org/src/libs/Anakunda/QobuzLib.min.js
// @require      https://openuserjs.org/src/libs/Anakunda/gazelleLib.min.js
// ==/UserScript==

'use strict';

const isBrowse = document.location.pathname.includes('/download-streaming-albums');
const isSearch = document.location.pathname.endsWith('/search');
const urlParams = new URLSearchParams(document.location.search);
const isAnchoredUrl = ['results', 'main-column', 'search-results-page', 'search-tree']
	.some(id => document.location.hash.startsWith('#' + id));

if (isAnchoredUrl && (/*isBrowse || */isSearch)) return;

const ipregistryToken = GM_getValue('ipregistry_token', '');
const ipifyToken = GM_getValue('ipify_token', '');
const processedColor = '#dcd5c1';
const scrapeDelay = 1000;
const pathParser = /^\/(?:index\.php\/)?(?:(\w{2}-\w{2})\/)?(?:(.+)\/)download-streaming-albums(?:\/(.+)?)?$/i;
const marketExtractor = /\/(\w{2}-\w{2})\//;
const vaParser = /^(?:Various(?:\s+Artists)?|Varios(?:\s+Artistas)?|V\/?A|\<various\s+artists\>|Různí(?:\s+interpreti)?)$/i;
const siteApiTimeframeStorageKey = 'AJAX time frame';
const targetCurrency = GM_getValue('target_currency');
const filtersKeyname = isBrowse ? 'browse_filters' : isSearch ? 'search_filters' : null;
const removeCheckedInstantly = GM_getValue('remove_checked_instantly', true);
const cachedApiKeys = ['media_count', 'tracks_count', 'duration', 'hires', 'parental_warning', 'label'];

Date.prototype.getDateValue = function() {
	return Math.floor((this.getTime() / 1000 / 60 - this.getTimezoneOffset()) / 60 / 24);
};

let today = new Date().getDateValue(), lastLookup = null, gazelleCounter = 0,
		redWorkers = [ ], dzApiTimeFrame = { }, siteArtistsCache = new Map,
		notSiteArtistsCache = new Set, hideUnavailable = false,
		onlyAvailOnDeezer = false, onlyReadOnDeezer = false,
		redacted_api_key = GM_getValue('redacted_api_key'),
		markets = GM_getValue('markets'),
		categories = GM_getValue('categories'),
		_QobuzGenresRating = GM_getValue('genres_rating'),
		labelStyles = GM_getValue('label_styles'),
		lpThreshold = GM_getValue('lp_threshold', 28 * 60),
		priceThreshold = GM_getValue('price_threshold', 9.00),
		singleThreshold = GM_getValue('single_threshold', 10 * 60),
		filterSearches = GM_getValue('filter_searches', true),
		page = parseInt(urlParams.get('page')) || 1,
		pageControls = isBrowse ? document.querySelector('div.search-results > div.pagination > ul') : null,
		nextPage = pageControls && pageControls.querySelector(':scope > li:last-of-type > a');
if (nextPage != null && nextPage.textContent != '→') nextPage = null;

function saveRlsCacheApi() {
	if (rlsCacheApi.size > 0)
		rlsCacheApi.forEach((val, key) => { if (!(val.releaseDate >= today)) rlsCacheApi.delete(key) });
	//if ('rlsCacheApi' in window.localStorage) delete window.localStorage.rlsCacheApi;
	window.localStorage.rlsCacheApi = JSON.stringify(Array.from(rlsCacheApi));
	console.debug('API cache size:', rlsCacheApi.size, '(' + formattedSize(window.localStorage.rlsCacheApi.length) + ')');
}
function saveRlsCacheHtml() {
	if (rlsCacheHtml.size > 0)
		rlsCacheHtml.forEach((val, key) => { if (!(val.releaseDate >= today)) rlsCacheApi.delete(key) });
	window.localStorage.rlsCacheHtml = JSON.stringify(Array.from(rlsCacheHtml));
	console.debug('HTML cache size:', rlsCacheHtml.size, '(' + formattedSize(window.localStorage.rlsCacheHtml.length) + ')');
}
function saveRlsCaches() { saveRlsCacheApi(); saveRlsCacheHtml(); }

try {
	var rlsCacheApi = new Map(JSON.parse(window.localStorage.rlsCacheApi));
	console.debug('API cache size:', rlsCacheApi.size, '(' + formattedSize(window.localStorage.rlsCacheApi.length) + ')');
} catch(e) { rlsCacheApi = new Map }
try {
	var rlsCacheHtml = new Map(JSON.parse(window.localStorage.rlsCacheHtml));
	console.debug('HTML cache size:', rlsCacheHtml.size, '(' + formattedSize(window.localStorage.rlsCacheHtml.length) + ')');
} catch(e) { rlsCacheHtml = new Map }

let dynFilters = GM_getValue(filtersKeyname, { });
if (isSearch && !(urlParams.get('page') > 1)) for (let key in dynFilters) dynFilters[key] = false;

let processedItems = GM_getValue('processed_items');
if (processedItems) try {
	if (!Array.isArray(processedItems)) processedItems = JSON.parse(processedItems);
	processedItems = new Map(processedItems.filter(entry => entry[1] >= today/* && entry[1] < 10**12*/));
} catch(e) { }
if (!(processedItems instanceof Map)) processedItems = new Map;

class XRates {
	constructor(targetCurrency) {
		function safeCurrencyCode(currency) {
			if (/^[A-Z]{3}$/i.test(currency)) return currency.toUpperCase();
			console.warn('XRates: invalid currency code used (' + currency + ')');
			return 'USD';
		}
		const methods = {
			targetCurrency: safeCurrencyCode(targetCurrency || 'USD'),
			getPrice: function(amount, fromCurrency, toCurrency) {
				fromCurrency = safeCurrencyCode(fromCurrency || this.targetCurrency);
				if (!(fromCurrency in this)) console.warn('XRates: initial currency rate missing (' + fromCurrency + ')');
				toCurrency = safeCurrencyCode(toCurrency || this.targetCurrency);
				if (!(toCurrency in this)) console.warn('XRates: target currency rate missing (' + toCurrency + ')');
				return toCurrency != fromCurrency ? amount * this[toCurrency] / this[fromCurrency] : amount;
			},
		};

		function getRatesFromXE() {
			const loadJSON = auth => globalXHR('https://www.xe.com/api/protected/midmarket-converter/', {
				responseType: 'json',
				headers: { Authorization: auth },
			}).then(({response}) => {
				console.assert(Object.keys(response.rates).length > 0, 'Object.keys(response.rates).length > 0');
				console.log('XE currency rates:', response.rates);
				return Object.assign(response.rates, methods);
			});
			const findhAuth = reason => globalXHR('https://www.xe.com/').then(({document}) =>
					Promise.all(Array.from(document.body.querySelectorAll(':scope > script[async]')).map(script => {
				try { var url = new URL(script.src); url.hostname = 'www.xe.com'; } catch(e) { return Promise.resolve(null) }
				return globalXHR(url, { responseType: 'application/javascript' }).then(function({responseText}) {
					let auth = /\("Authorization",\s*(.+?)\)[\,\;]/i.exec(responseText);
					if (auth != null) try { return eval(auth[1]) } catch(e) { }
					if ((auth = /\("Authorization",.*\("(\w+)"\){4}/i.exec(responseText)) == null) return null;
					if (!RegExp.lastMatch.includes('"lodestar"')) console.info('XE auth keyword changed:', RegExp.lastMatch);
					return 'Basic ' + btoa('lodestar:' + auth[1]);
				}).catch(reason => {
					console.warn(url + ':', reason);
					return null;
				});
			})).then(results => results.filter((val, ndx, arrRef) => val && arrRef.indexOf(val) == ndx)).then(tokens => {
				if (tokens.length <= 0) return Promise.reject('auth string not found');
				if (tokens.length > 1) console.warn('XE: More than one auth strings captured');
				console.log('XE captured auth string(s):', tokens);
				let div = document.createElement('DIV');
				div.id = 'xe-new-token-notifier';
				div.style = `
position: fixed; right: 20px; bottom: 20px;
padding: 6pt; z-index: 999;
color: black; background-color: papayawhip;
border: 5px solid darkorange;
font: 600 10pt Verdana, sans-serif;
transition: opacity 1s ease 0s; opacity: 0;
`;
				div.textContent = 'XE auth token successfully ' + (reason ? 'refreshed' : 'obtained');
				window.document.body.append(div);
				setTimeout(div => { div.style.opacity = 1 }, 0, div);
				setTimeout(function(div) {
					div.style.opacity = 0;
					setTimeout(div => { div.remove }, 0, div);
				}, (GM_getValue('toast_timeout', 5) + 1) * 1000, div);
				return (window.localStorage.xeAuth = tokens[0]);
			}));
			if (!('xeAuth' in window.localStorage) || !/^\w+\s+\S+$/.test(window.localStorage.xeAuth))
				return findhAuth().then(loadJSON);
			return loadJSON(window.localStorage.xeAuth).catch(reason => {
				console.warn('Cached XE auth failed:', reason);
				if (!/^(?:HTTP error 403)\b/.test(reason)) return Promise.reject(reason);
				console.log('XE cached token ' + window.localStorage.xeAuth + ' refused, trying to capture new one...');
				if (window.localStorage.xeAuth) delete window.localStorage.xeAuth;
				return findhAuth(reason).then(loadJSON);
			});
		}

		const getratesFromXRates = () => globalXHR('https://www.x-rates.com/table/?' + new URLSearchParams({
			from: 'USD',
			amount: 1,
		}).toString()).then(function({document}) {
			let result = { USD: 1 };
			for (let a of document.querySelectorAll('table.ratesTable > tbody > tr > td.rtRates > a')) {
				if (!a.pathname.startsWith('/graph/')) continue;
				const params = new URLSearchParams(a.search);
				if (params.get('from') == 'USD') result[params.get('to')] = parseFloat(a.textContent);
			}
			return Object.keys(result).length > 1 ? result : Promise.reject('invalid page structure');
		}).catch(function(reason) {
			console.warn('XRates table fetch failed (' + reason + ')');
			const currencies = [
				'USD', 'CAD',
				'JPY', 'CNY', 'RUB',
				'AUD', 'NZD',
				'EUR', 'GBP', 'CHF', 'SEK', 'NOK', 'DKK', 'CZK',
			];
			return Promise.all(currencies.map(currency => globalXHR('https://www.x-rates.com/calculator/?' + new URLSearchParams({
				from: 'USD',
				to: currency,
				amount: 1,
			}).toString()).then(function(response) {
				let span = response.document.body.querySelector('span.ccOutputRslt');
				if (span == null) {
					console.warn('XRates conversion result parsing failed (' + response.finalUrl + ')');
					return undefined;
				}
				let result = /\b(\d+(?:[\.\,]\d+)?)\b/.exec(span.textContent);
				if (result != null) return parseFloat(result[1].replace(',', '.'));
				console.warn('XRates conversion result parsing failed', span.textContent);
			}))).then(function(results) {
				let result = { };
				results.forEach((ratio, index) => { result[currencies[index]] = ratio });
				return result;
			});
		}).then(result => {
			console.log('XRates currency rates:', result);
			return Object.assign(result, methods);
		});

		this.promise = getRatesFromXE().catch(getratesFromXRates);
	}

	then(resolve, reject) { return this.promise.then(resolve, reject) }
	//get then() { return this.promise.then }
}
let xRates = new XRates('EUR');
//xRates.then(rates => { console.log('XRates:', rates) }, reason => { console.error('Failed to load Xrates data:', reason) });

function queryDeezerAPI(action, params) {
	return action ? new Promise(function(resolve, reject) {
		const t0 = Date.now(), safeTimeFrame = 5000 + GM_getValue('deezer_quota_reserve', 100);
		let dzUrl = new URL('https://api.deezer.com/' + action), retryCounter = 0, quotaCounter = 0;
		if (params && typeof params == 'object') try { dzUrl.search = new URLSearchParams(params) }
			catch(e) { console.error(e, params) }
		else if (params != undefined) dzUrl.pathname += '/' + params.toString();
		//console.debug('Deezer query URL:', url);
		requestInternal();

		function requestInternal() {
			const requestStart = Date.now();
			if (!dzApiTimeFrame.timeLock || requestStart > dzApiTimeFrame.timeLock) {
				dzApiTimeFrame.timeLock = requestStart + safeTimeFrame;
				dzApiTimeFrame.requestCounter = 1;
			} else ++dzApiTimeFrame.requestCounter;
			const queueSnapshot = {
				requestStart: requestStart,
				timeLock: dzApiTimeFrame.timeLock,
				position: dzApiTimeFrame.requestCounter,
				frameLength: safeTimeFrame,
			};
			if (dzApiTimeFrame.requestCounter <= 50) GM_xmlhttpRequest({
				method: 'GET',
				url: dzUrl,
				responseType: 'json',
				headers: {
					'Accept': 'application/json',
					'Accept-Language': 'en-US, en',
					'X-Requested-With': 'XMLHttpRequest',
				},
				onload: function(response) {
					if (response.status < 200 || response.status >= 400) return reject(defaultErrorHandler(response));
					if (!response.response.error) {
						let dt = Date.now() - t0;
						resolve(response.response);
						if (retryCounter > 0) console.debug('Deezer API request fulfilled after',
							retryCounter, 'retries and', quotaCounter, 'postponements in', dt, 'ms');
					} else if (response.response.error.code == 4) {
						setTimeout(requestInternal, 100);
						console.warn('Deezer API semaphore failed:', queueSnapshot, dzApiTimeFrame, ++retryCounter);
					} else reject(response.response.error.message);
				},
				onerror: response => { reject(defaultErrorHandler(response)) },
				ontimeout: response => { reject(defaultTimeoutHandler(response)) },
			}); else {
				setTimeout(requestInternal, dzApiTimeFrame.timeLock - requestStart);
				++quotaCounter;
			}
		}
	}) : Promise.reject('Action missing');
}

function queryQobuzAPI(endPoint, params, data) {
	const qobuzAPI = (function() {
		if ('qobuzAPIs' in window.sessionStorage) try {
			var qobuzAPIs = JSON.parse(window.sessionStorage.qobuzAPIs);
			if (qobuzAPIs.length > 0) return Promise.resolve(qobuzAPIs[qobuzAPIs.length - 1]);
		} catch(e) { delete window.sessionStorage.qobuzAPIs }
		return globalXHR('https://play.qobuz.com/login').then(function({document}) {
			let script = document.body.querySelector('script[src]:last-of-type');
			if (script == null) return Promise.reject('invalid document structure');
			let url = new URL(script.src);
			url.hostname = 'play.qobuz.com';
			return globalXHR(url, { responseType: 'application/javascript' });
		}).then(function({responseText}) {
			qobuzAPIs = responseText.match(/\b(?:n\.qobuzapi)=(\{.*?\})/g)
				.map(s => eval('(' + /\b(?:n\.qobuzapi)=(\{.*?\})/.exec(s)[1] + ')'));
			if (qobuzAPIs.length <= 0) return Promise.reject('invalid format (bundle.js)');
			window.sessionStorage.qobuzAPIs = window.localStorage.qobuzAPIs = JSON.stringify(qobuzAPIs);
			return qobuzAPIs[qobuzAPIs.length - 1];
		}).catch(function(reason) {
			console.warn('Qobuz APIs extraction failed, trying to reuse last cached record', reason);
			if ('qobuzAPIs' in window.localStorage) try {
				qobuzAPIs = JSON.parse(window.localStorage.qobuzAPIs);
				if (qobuzAPIs.length > 0) return qobuzAPIs[qobuzAPIs.length - 1];
			} catch(e) { delete window.localStorage.qobuzAPIs }
			return Promise.reject(reason);
		});
	})();

	function getUser(useCache = true) {
		const uid = GM_getValue('account_uid'), password = GM_getValue('account_password');
		if ('qobuzUserInfo' in window.localStorage) try {
			let userInfo = JSON.parse(window.localStorage.qobuzUserInfo);
			if (uid && userInfo.user.login.toLowerCase() != uid.toLowerCase()) throw 'User credentials changed';
			if (!userInfo.user_auth_token) throw 'User info incomplete';
			if (useCache) {
				//console.log('Qobuz user info re-used:', userInfo);
				return Promise.resolve(userInfo);
			}
		} catch(e) { delete window.localStorage.qobuzUserInfo }
		if (!uid || !password) return Promise.reject('Insufficient user credentials');
		return qobuzAPI.then(qobuzAPI => localXHR(qobuzAPI.base_url + qobuzAPI.base_method + 'user/login', {
			responseType: 'json',
			headers: { 'X-App-Id': qobuzAPI.app_id }
		}, new URLSearchParams({ email:	uid, password:	password }))).then(function(response) {
			console.log('Qobuz login successfull:', response);
			if (!response.user_auth_token) throw 'User info incomplete';
			window.localStorage.qobuzUserInfo = JSON.stringify(response);
			return response;
		});
	}

	return endPoint ? getUser(true).then(user => qobuzAPI.then(function(qobuzAPI) {
		let url = new URL(qobuzAPI.base_method + endPoint, qobuzAPI.base_url);
		if (params && typeof params == 'object') url.search = new URLSearchParams(params);
		return localXHR(url, {
			responseType: 'json',
			headers: {
				'X-App-Id': qobuzAPI.app_id,
				'X-User-Auth-Token': user.user_auth_token,
			},
		}, data);
	})) : Promise.reject('API endpoint missing');
}

function saveProcessedItems() {
	today = new Date().getDateValue();
	processedItems.forEach(function(val, key) { if (!val || val < today) processedItems.delete(key) });
	GM_setValue('processed_items', Array.from(processedItems));
	if (pi instanceof HTMLElement) pi.textContent = processedItems.size;
}

function genPageUrl(pageNo) {
	let url = new URL(document.location);
	if (pageNo) url.searchParams.set('page', pageNo);
	url.searchParams.sort();
	return url;
}

function getIdFromUrl(elem) {
	return elem instanceof HTMLAnchorElement && elem.pathname.split('/').pop().toLowerCase() || null;
}

function getReleaseYear(elem) {
	if (!(elem instanceof HTMLElement)) {
		console.warn('Invalid argument');
		return undefined;
	}
	let y = /\b(\d{4})\b/.exec(elem.childNodes[2].textContent);
	y = parseInt(y != null ? y[1] : !isNaN(y = new Date(elem.childNodes[2].textContent)) ?
		y.getFullYear() : elem.childNodes[2].textContent);
	return y >= 1900 ? y : null;
}

function getReleaseDate(elem) {
	if (!(elem instanceof HTMLElement)) {
		console.warn('Invalid argument');
		return undefined;
	}
	if ((elem = elem.querySelector('p.data.overflow')) != null) return new Date([
		[/^.*\b(?:released|le|am|el|il|op)\s+/i, ''],
		[/\b(?:de)\s+/g, ''],
		[/\s*\|\s*$/g, ''],
		[/\./g, ''],
		// translations
		[/\b(?:janvier|Januar|enero|gennaio|januari)\b/i, 'January'],
		[/\b(?:février|Februar|febrero|febbraio|februari)\b/i, 'February'],
		[/\b(?:mars|März|marzo|maart)\b/i, 'March'],
		[/\b(?:avril|abril|aprile)\b/i, 'April'],
		[/\b(?:mai|Mai|mayo|maggio|mei)\b/i, 'May'],
		[/\b(?:juin|Juni|junio|giugno)\b/i, 'June'],
		[/\b(?:juillet|Juli|julio|luglio)\b/i, 'July'],
		[/\b(?:août|August|agosto|augustus)\b/i, 'August'],
		[/\b(?:septembre|septiembre|settembre)\b/i, 'September'],
		[/\b(?:octobre|Oktober|octubre|ottobre)\b/i, 'October'],
		[/\b(?:novembre|noviembre|novembre)\b/i, 'November'],
		[/\b(?:décembre|Dezember|diciembre|dicembre)\b/i, 'December'],
	].reduce((r, def) => r.replace(...def), elem.childNodes[2].textContent)).getDateValue();
	console.warn('Unexpected item structure (p.data.overflow == NULL)');
}

function getSizeFromString(str, returnAs = undefined) {
	if (typeof str != 'string') return 0;
	let matches = /\b(\d+(?:\.\d+)?)\s*([KMGTPEZY]?)I?B\b/.exec(str.replace(',', '.').toUpperCase());
	if (matches == null) return 0;
	const prefixes = Array.from('KMGTPEZY');
	let size = parseFloat(matches[1]);
	let fromIndex = prefixes.indexOf(matches[2]);
	let toIndex = /^([KMGTPEZY]?)(?:i?B)?$/i.test(returnAs) ? prefixes.indexOf(RegExp.$1.toUpperCase()) : 1;
	let result = size * Math.pow(2, (fromIndex - toIndex) * 10);
	return toIndex >= 0 ? result : Math.round(result);
}

function formattedSize(size) {
	return size < 1024**1 ? Math.round(size) + ' B'
		: size < 1024**2 ? (Math.round(size * 10 / 2**10) / 10) + ' KiB'
		: size < 1024**3 ? (Math.round(size * 100 / 2**20) / 100) + ' MiB'
		: size < 1024**4 ? (Math.round(size * 100 / 2**30) / 100) + ' GiB'
		: size < 1024**5 ? (Math.round(size * 100 / 2**40) / 100) + ' TiB'
		: (Math.round(size * 100 / 2**50) / 100) + ' PiB';
}

function makeTimeString(duration, forceSign = false) {
	let t = Math.abs(Math.round(duration));
	let H = Math.floor(t / 60 ** 2);
	let M = Math.floor(t / 60 % 60);
	let S = t % 60;
	return (duration < 0 ? '-' : duration > 0 && forceSign ? '+' : '') +
		(H > 0 ? H + ':' + M.toString().padStart(2, '0') : M.toString()) + ':' + S.toString().padStart(2, '0');
}

function timeStringToTime(str) {
	if (!/(-\s*)?\b(\d+(?::\d{2})*(?:\.\d+)?)\b/.test(str)) return null;
	let t = 0, a = RegExp.$2.split(':');
	while (a.length > 0) t = t * 60 + parseFloat(a.shift());
	return RegExp.$1 ? -t : t;
}

function setDefaultMarkets() {
	GM_setValue('markets', markets = [
		'nl-nl', 'ch-fr', 'gb-en', 'fr-fr', 'us-en', 'de-de',
		'es-es', 'it-it', 'at-de', 'be-fr', 'lu-fr', 'ie-en',
		'dk-en', 'fi-en', 'se-en',
		'no-en',
		'au-en', 'nz-en',
	]);
}

function setDefaultCategories() {
	GM_setValue('categories', categories = [
		'quality/HD-24-bit',
		'genre/pop-rock',
		'genre/electro',
		'genre/soul-funk-rap',
		'genre/alternatif-et-inde',
		'genre/ambiant',
		'genre/folk',
// 		'genre/blues-country-folk',
// 		'genre/musiques-du-monde',
// 		'genre/jazz',
// 		'genre/musique-de-films',
	]);
}
function setDefaultDiscounts() {
	GM_setValue('genres_rating', _QobuzGenresRating = {
		0: [
			'Acid Jazz', 'Acid jazz',
			'Alternative & Indie', 'Alternatif et Indé', 'Alternativ und Indie', 'Alternativa & Indie', 'Musica alternativa e indie', 'Alternative en Indie',
			'Ambient', 'Ambientes',
			'Blues',
			'Chill-out', 'Downtempo',
			'Crossover',
			'Dance',
			'Dancehall',
			'Disco',
			'Dub',
			'Electronic', 'Electronic/Dance', 'Électronique', 'Electrónica', 'Elettronica',
			/*'Electronic', */'Musique électronique', 'Elektronische Musik', 'Música electrónica', 'Musica elettronica', 'Elektronische muziek',
			'Experimental', 'Électronique ou concrète', 'Elektronische Musik oder Musique concrète', 'Electrónica o musique concrète', 'Musica elettronica/concreta', 'Elektronische muziek of Musique Concrète',
			'Funk',
			'Hard Rock', 'Hard rock', 'Hardrock',
			'House',
			'Indie Pop', 'Pop indé', 'Indie-Pop', 'Pop indie', 'Indie pop', 'Indiepop',
			'Metal',
			'Minimal Music', 'Musique minimaliste', 'Música minimalista', 'Musica minimalista', 'Minimalistische muziek',
			'Miscellaneous', 'Divers',
			'Pop',
			'Pop/Rock',
			'Progressive Rock', 'Rock progressif', 'Rock progresivo', 'Rock progressivo', 'Progressieve rock',
			'Punk / New Wave', 'Punk - New Wave', 'Punk – New Wave', 'Punk/New wave', 'Punk en New Wave',
			'R&B',
			'Reggae',
			'Rock',
			'Rockabilly',
			'Ska & Rocksteady', 'Ska e rocksteady', 'Ska en Rocksteady',
			'Soul',
			'Soul/Funk/R&B', 'R&B/Soul',
		],
		0.05: [
			'Afrobeat',
			'Blues/Country/Folk', 'Blues/country/folk',
			'Folk', 'Folk/Americana',
			'Lounge',
		],
		0.10: [
			'Contemporary Jazz', 'Jazz contemporain', 'Modern Jazz', 'Jazz contemporáneo', 'Jazz contemporaneo', 'Moderne jazz',
			'Europe', 'Europa',
			'French Artists', 'Interprètes de chanson française', 'Französische Chanson-Sänger', 'Intérpretes de chanson francesa', 'Artisti francesi', 'Zangers van Franse chansons',
			'French Music', 'Chanson française', 'Französischer Chanson', 'Chanson francesa', 'Musica francese', 'Franse chansons', 'Variété francophone',
			'French Rock', 'Rock français', 'Französischer Rock', 'Rock francés', 'Rock francese', 'Franse rock',
			'Irish Celtic', 'Irish celtic', 'Irisch-keltische Musik', 'Música celta irlandesa', 'Musica celtica irlandese', 'Iers Keltisch',
			'Irish Pop Music', 'Irish popmusic', 'Irische Popmusik', 'Música pop irlandesa', 'Musica pop irlandese', 'Ierse popmuziek',
			'Jazz Fusion & Jazz Rock', 'Jazz fusion & Jazz rock', 'Jazz Fusion & Jazzrock', 'Jazz fusión & Jazz rock', 'Fusion & Jazz rock', 'Jazz fusion en jazz rock',
			'Jazz',
			'Latin Jazz', 'Latin jazz',
			'Vocal Jazz', 'Jazz vocal', 'Jazzgesang', 'Vocal jazz', 'Vocale jazz',
		],
		0.20: [
			'Africa', 'Afrique', 'Afrika', 'África',
			'Asia', 'Asie', 'Asien', 'Azië',
			'Bebop', 'Be Bop',
			'Bossa Nova & Brazil', 'Bossa Nova & Brésil', 'Bossa Nova & brasilianische Musik', 'Bossa nova & Brasil', 'Bossa nova e musica brasiliana ', 'Bossanova en Brazilië',
			'Celtic', 'Celtique', 'Keltische Musik', 'Celta', 'Musica celtica', 'Keltisch',
			'Cool Jazz', 'Cool jazz', 'Cooljazz',
			'Country',
			'Crooners', 'Crooner', 'Musica crooner',
			'Dixieland', 'Dixie',
			'Drum & Bass', 'Drum \'n\' bass',
			'Eastern Europe', 'Europe de l\'Est', 'Osteuropa', 'Europa del Este', 'Europa dell\'est', 'Oost-Europa',
			'Fado',
			'Film Soundtracks', 'Bandes originales de films', 'Original Soundtrack', 'Bandas sonoras de cine', 'Colonne sonore', 'Originele soundtracks',
			'Flamenco',
			'Free Jazz & Avant-Garde', 'Free jazz & Avant-garde', 'Free Jazz & Avantgarde', 'Free jazz & Vanguardia', 'Free jazz et jazz d\'avanguardia', 'Free jazz & Avant-garde jazz',
			'Greece', 'Grèce', 'Griechenland', 'Grecia', 'Griekenland',
			'Gypsy', 'Gipsy', 'Musik der Roma', 'Gitano', 'Zigeunermuziek',
			'Gypsy Jazz', 'Jazz manouche', 'Gypsy-Jazz', 'Gipsy jazz',
			'Indian Music', 'Musique indienne', 'Indische Musik', 'Música india', 'Musica indiana', 'Indiase muziek',
			'Ireland', 'Irlande', 'Irland', 'Irlanda', 'Ierland',
			'Italy', 'Italie', 'Italien', 'Italia', 'Italië',
			'Latin America', 'Amérique latine', 'Lateinamerika', 'Latinoamérica', 'America latina', 'Latijns-Amerika',
			'Maghreb', 'Magreb', 'Noord-Afrika',
			'North America', 'Amérique du Nord', 'Nordamerika', 'Norteamérica', 'Amercia del nord', 'Noord-Amerika',
			'Oriental Music', 'Orient', 'Oriente', 'Musica orientale', 'Oosters',
			'Portugal', 'Portogallo',
			'Ragtime',
			'Raï',
			'Russia', 'Russie', 'Russland', 'Rusia', 'Rusland',
			'Salsa',
			'Scottish', 'Ecosse', 'Schottland', 'Escocia', 'Scozia', 'Schotland',
			'Soundtracks', 'Film', 'Cine', 'Cinema', 'Soundtrack',
			'Spain', 'Espagne', 'Spanien', 'España', 'Spagna', 'Spanje',
			'Swiss Folk Music', 'Musique folklorique Suisse', 'Schweizer Volksmusik', 'Música folclórica suiza', 'Musica folclorica svizzera', 'Zwitserse volksmuziek',
			'Tango',
			'Traditional Jazz & New Orleans', 'Jazz traditionnel & New Orleans', 'Klassischer Jazz & New-Orleans-Jazz', 'Jazz tradicional & Nueva Orleans', 'Jazz tradizionale & New Orleans', 'Traditionele jazz en dixieland',
			'World', 'Musiques du monde', 'Aus aller Welt', 'World music', 'Wereldmuziek',
			'Yiddish & Klezmer', 'Jiddische Musik & Klezmer', 'Musica yiddish e klezmer', 'Jiddisch en klezmer',
			'Zouk & Antilles', 'Zouk & Musik von den Antillen', 'Zouk & Antillas', 'Musica zouk e Antille', 'Zouk en Antilliaans',
		],
		0.30: [
			'Ambient/New Age', 'Ambiance', /*'Lounge', 'Ambientes', */'Musica d\'ambiente/New Age', 'Ambient / New Age / Easy Listening',
			'Christmas Music', 'Musiques de Noël', 'Weihnachtsmusik', 'Músicas navideñas', 'Canzoni di Natale', 'Kerstmuziek',
			'International Pop', 'Variété internationale', 'Internationaler Pop', 'Variété internacional', 'Pop internazionale', 'Internationaal variété',
			'Musical Theatre', 'Comédies musicales', 'Musical', 'Comedias musicales', 'Musicals',
			'New Age', 'Musica new Age', 'New age',
			'Retro French Music', 'Chanson française rétro', 'Französisches Retro-Chanson', 'Chanson francesa retro', 'Musica francese retrò', 'Oude Franse chansons',
			'Trance',
			'Trip Hop', 'Triphop',
			'TV Series', 'Séries TV', 'TV-Serien', 'Series de televisión', 'Serie TV', 'Tv-series',
			'Video Games', 'Jeux vidéo', 'Computerspiele', 'Vídeojuegos', 'Video Giochi', 'Videogames',
		],
		0.40: [
			'Art Songs', 'Lieder', 'Kunstlieder', 'Liederen',
			'Relaxation', 'Entspannung', 'Relajación', 'Musica rilassante', 'Ontspanning',
		],
		0.50: [
			'Gospel',
			'Military Music', 'Musique militaire', 'Militärmusik', 'Música militar', 'Musica militare', 'Militaire muziek',
			'Turkey', 'Turquie', 'Türkei', 'Turquía', 'Turchia', 'Turkije',
		],
		Infinity: [ // Fully hide these
			'Accordion', 'Accordéon', 'Akkordeon', 'Acordeón', 'Fisarmonica', 'Accordeon',
			'Art Songs, Mélodies & Lieder', 'Mélodies & Lieder', 'Französische Mélodies und Kunstlieder', 'Liederen',
			'Ballets', 'Ballett', 'Balletti', 'Balletten',
			'Bawdy songs', 'Chansons paillardes', 'Canciones gamberras', 'Canzoni licenziose', 'Schuine liedjes',
			'Cantatas (sacred)', 'Cantates sacrées', 'Geistliche Kantaten', 'Cantatas sacras', 'Cantate sacre', 'Religieuze cantates',
			'Cantatas (secular)', 'Cantates (profanes)', 'Kantaten (weltlich)', 'Cantatas (profanas)', 'Cantate (profane)', 'Cantates (wereldlijk)',
			'Cello Concertos', 'Concertos pour violoncelle', 'Cellokonzerte', 'Conciertos para violonchelo', 'Concerti per violoncello', 'Concerten voor cello',
			'Cello Solos', 'Violoncelle solo', 'Cellosolo', 'Violonchelo solo', 'Assoli per violoncello', 'Cello solo',
			'Chamber Music', 'Musique de chambre', 'Kammermusik', 'Música de cámara', 'Musica da camera', 'Kamermuziek',
			'Children', 'Enfants', 'Kinder', 'Infantil', 'Infanzia', 'Kinderen',
			'Choirs (sacred)', 'Chœurs sacrés', 'Geistliche Chormusik', 'Coros sacros', 'Cori sacri', 'Religieuze koormuziek',
			'Choral Music (Choirs)', 'Musique chorale (pour chœur)', 'Chorwerk (für den Chor)', 'Música coral (para coro)', 'Musica corale', 'Koormuziek',
			'Cinema Music', 'Musiques pour le cinéma', 'Filmmusik', 'Bandas sonoras', 'Musiche per il cinema', 'Soundtrack',
			'Classical', 'Classique', 'Klassik', 'Clásica', 'Classica', 'Klassiek',
			'Concertos for trumpet', 'Concertos pour trompette', 'Trompetenkonzerte', 'Conciertos para trompeta', 'Concerti per tromba', 'Concerten voor trompet',
			'Concertos for wind instruments', 'Concertos pour instruments à vent', 'BläserKonzerte', 'Conciertos para instrumentos de viento', 'Concerti per strumenti a fiato', 'Concerten voor blaasinstrumenten',
			'Concertos', 'Musique concertante', 'Instrumentalmusik', 'Música concertante', 'Musica concertante', 'Concertmuziek',
			'Duets', 'Duos', 'Duette', 'Dúos', 'Duetti', 'Duo´s',
			'Dutch', 'Néerlandais', 'Niederländisch', 'Neerlandés', 'Olandese', 'Nederlands',
			'Educational', 'Educatif', 'Bildung', 'Educativa', 'Musica educativa', 'Educatief',
			'Educational', 'Pédagogie', 'Pädagogik', 'Pedagogía', 'Musica educativa', 'Pedagogiek',
			'English', 'Anglais', 'Englisch', 'Inglés', 'Inglese', 'Engels',
			'French', 'Français', 'Französisch', 'Francés', 'Francese', 'Frans',
			'Full Operas', 'Intégrales d\'opéra', 'Gesamtaufnahmen von Opern', 'Integrales de ópera', 'Opere integrali', 'Volledige opera\'s',
			'German', 'Allemand', 'Deutsch', 'Alemán', 'Tedesco', 'Duits',
			'Germany', 'Allemagne', 'Deutsche Musik', 'Alemania', 'Germania', 'Duitsland',
			'Historical Documents', 'Documents historiques', 'Historische Dokumente', 'Documentos históricos', 'Documenti storici', 'Historische documenten',
			'Humour', 'Humor', 'Umorismo',
			'Humour/Spoken Word', 'Comedy/Other', 'Diction', 'Hörbücher', 'Audiolibros', 'Spoken Word', 'Cabaret/ Komedie / Luisterboek',
			'Karaoke', 'Karaoké',
			'Keyboard Concertos', 'Concertos pour clavier', 'Klavierkonzerte', 'Conciertos para tecla', 'Concerti per tastiera', 'Concerten voor klavier',
			'Lieder (German)', 'Lieder (Allemagne)', 'Kunstlieder (Deutschland)', 'Lieder (Alemania)', 'Lieder (Germania)', 'Liederen (Duitsland)',
			'Literature', 'Littérature', 'Literatur', 'Literatura', 'Letteratura', 'Literatuur',
			'Masses, Passions, Requiems', 'Messes, Passions, Requiems', 'Messen, Passionen, Requiems', 'Misas, Pasiones, Réquiems', 'Messe, Passioni, Requiem', 'Missen, passies, requiems',
			'Mélodies (England)', 'Mélodies (Angleterre)', 'Mélodies (Inglaterra)', 'Mélodies (Inghilterra)', 'Liederen (Engeland)',
			'Mélodies (French)', 'Mélodies (France)', 'Französische Mélodies (Frankreich)', 'Mélodies (Francia)', 'Liederen (Frankrijk)',
			'Mélodies (Northern Europe)', 'Mélodies (Europe du Nord)', 'Mélodies (Nordeuropa)', 'Mélodies (Europa del Norte)', 'Mélodies (Europa del nord)', 'Liederen (Noord-Europa)',
			'Mélodies', 'Liederen',
			'Music by vocal ensembles', 'Musique pour ensembles vocaux', 'Musik für Vokalensembles', 'Música para conjuntos vocales', 'Musica per insiemi vocali', 'Muziek voor vocale ensembles',
			'Musique Concrète', 'Musique concrète', 'Musique concréte', 'Musica concreta',
			'Opera Extracts', 'Extraits d\'opéra', 'Opernauszüge', 'Fragmentos de ópera', 'Estratti d\'opera', 'Operafragmenten',
			'Opera', 'Opéra', 'Oper', 'Ópera',
			'Operettas', 'Opérette', 'Operette', 'Opereta', 'Operetta',
			'Oratorios (secular)', 'Oratorios profanes', 'Weltliche Oratorien', 'Oratorios profanos', 'Oratori profani', 'Wereldlijke oratoria',
			'Overtures', 'Ouvertures', 'Ouvertüren', 'Oberturas', 'Overture',
			'Quartets', 'Quatuors', 'Quartette', 'Cuartetos', 'Quartetti', 'Kwartetten',
			'Quintets', 'Quintettes', 'Quintette', 'Quintetos', 'Quintetti', 'Kwintetten',
			'Rap', 'Hip-Hop', 'Rap/Hip-Hop', 'Hip-Hop/Rap',
			'Sacred Oratorios', 'Oratorios sacrés', 'Geistliche Oratorien', 'Oratorios sacros', 'Oratori sacri',
			'Sacred Vocal Music', 'Musique vocale sacrée', 'Geistliche Vokalmusik', 'Música vocal sacra', 'Musica vocale sacra', 'Religieuze vocale muziek',
			'Secular Vocal Music', 'Musique vocale profane', 'Weltliche Vokalmusik', 'Música vocal profana', 'Musica vocale profana', 'Wereldlijke vocale muziek',
			'Schlager',
			'Solo Piano', 'Piano solo', 'Klaviersolo', 'Assoli per pianoforte',
			'Stimmungsmusik ', 'Stimmungsmusik',
			'Stories and Nursery Rhymes', 'Contes et comptines', 'Märchen und Kinderreime', 'Cuentos & canciones infantiles', 'Racconti e filastrocche', 'Sprookjes en vertellingen',
			'Symphonic Music', 'Musique symphonique', 'Symphonieorchester', 'Música sinfónica', 'Musica sinfonica', 'Symfonische muziek',
			'Symphonic Poems', 'Poèmes symphoniques', 'Symphonische Dichtung', 'Poemas sinfónicos', 'Poemi sinfonici', 'Symfonische gedichten',
			'Symphonies', 'Symphonien', 'Sinfonías', 'Sinfonie', 'Symfonieën',
			'Techno',
			'Theatre Music', 'Musique de scène', 'Intermezzi', 'Música escénica', 'Musiche di scena', 'Toneelmuziek',
			'Trios', 'Tríos', 'Trii', 'Trio´s',
			'Violin Concertos', 'Concertos pour violon', 'Violinkonzerte', 'Conciertos para violín', 'Concerti per violino', 'Concerten voor viool',
			'Violin Solos', 'Violon solo', 'Violinensolo', 'Violín solo', 'Assoli per violino', 'Viool solo',
			'Vocal Music (Secular and Sacred)', 'Musique vocale (profane et sacrée)', 'Vokalmusik (weltlich und geistlich)', 'Música vocal (profana y sacra)', 'Musica vocale (sacra e profana)', 'Vocale muziek (wereldlijk en religieus)',
			'Vocal Recitals', 'Récitals vocaux', 'Gesangsrezitale', 'Recitales vocales', 'Recital vocali', 'Vocale recitals',
			'Volksmusik',
		],
	});
}
function setDefaultLabelStyles() {
	const normalizeName = name => name
		.replace(/\s+(?:Records|Recordings|Rec\.|Ltd\.?|Inc\.?|International|Intl\.)$/i, '') // common suffixes
		.replace(/[\.\*\+\?\(\)\{\}\[\]\\\/\^\$\|]/g, lit => '\\' + lit); //.replace(/[^\w\s\!\@\#\%\&\,\;\:\<\>\`\~\"]/g, lit => '\\' + lit);

	GM_setValue('label_styles', labelStyles = [
		[
			'/\\[PIAS\\]|^(?:Sub[\\-\\s]?Pop|4AD|Mute|Domino|Cooking Vinyl|Ninja Tune|Fat Possum|Mom\\s*\\+\\s*Pop|' +
			'One Little Indian|Ghostly International|Beggars Banquet|Drag City|Secretly Canadian|' +
			'Play It Again Sam|Temporary Residence|Loma Vista|City Slang|Third Man|Bella Union|Erased Tapes|Sacred Bones|' +
			'Atlantic|Western Vinyl|Geffen|tôt Ou tard|Terrible|Republic|Sire|' +
			'Caroline International|Heavenly|Because Music|ANTI|Epitaph|Merge|Fueled By Ramen|Nonesuch|Glassnote|' +
			'Warp|Polyvinyl|RVNG|Joyful Noise)\\b/i',
			'color: darkorange; font-weight: 900; text-shadow: 0 0 1px #0008;'
		], [
			'/^(?:EMI|Polydor|Reprise|InsideOut|InsideOutMusic|Islands|Rat Pak|Chrysalis|(?:New )?Rounder|Arista|Repertoire|' +
			'Mobile Fidelity Sound Lab|Cherry Red|Verve|Carpark|Ipecac|Roadrunner|Spinefarm|Prolifica|Nocut|Nettwerk|' +
			'Concord|Tapete|Fuzz Club|Columbia|Provogue|Pure Noise|Kscope|Ear\s?Music|Mercury|Yep Roc|Netwerk|Pelagic|' +
			'The Leaf|Rough Trade|The Leaf|Get Better|New West|Cosmicleaf|Fatcat|Fiction Records|Tru Thoughts' +
			'Mexican Summer|Cleopatra|Memphis Industries|Drabant Music|Shimmy[ \-]Disc|Gondwana|Sumerian|Partisan|' +
			'Wagram|Omnivore|New Amsterdam|Numero Group|BBE|Fire Records|Clouds Hill)\\b/i',
			'color: #00abae; font-weight: bold; text-shadow: 0 0 1px #0003;'
		], [
			'/^(?:Parlophone|Universal Music|BMG|ARIOLA|Decca|Warner|WM|Elektra|RCA|Capitol|Legacy|Rhino|Sony Music|Virgin)\\b/i',
			'color: dodgerblue; font-weight: normal;'
		], [ // Hard & Heavy
			'/^(?:Metal Blade|Frontiers|Nuclear Blast|Napalm|Signum|Century Media|Solid State|Season of Mist|Relapse|' +
			'Long Branch|Unique Leader|Mascot|Ripple|Massacre)\\b/i',
			'color: blueviolet; font-weight: normal;'
		], [ // Jazz & World
			'/^(?:Blue Note|ACT Music|ECM|Okeh|Chandos|Naxos|Motema|Edition)\\b/i',
			'color: darkcyan; font-weight: normal;'
		], [ // Electro & Dance
			'/^(?:Local Action|Amselcom|n5MD)\\b/i',
			'color: deeppink; font-weight: normal;'
		], [ // RED top1000 subscribed labels
			'/^(?:(?:' + [
				'L.I.E.S.', 'трип', 'STROOM 〰', 'Where To Now?', 'Numbers.', 'XPQ?', '[a:rpia:r]', 'InFiné', 'Presto!?',
				'فورتونا', 'Kitsuné', 'No \'Label\'', 'LACKREC.', 'Good Timin\'', 'Now That\'s What I Call Music!',
			].map(normalizeName).join('|') + ')|(?:' + [
				'Lobster Theremin', 'Warp Records', 'Giegling', 'Music from Memory', 'Whities / AD 93', 'Ilian Tape',
				'Long Island Electrical Systems', 'Ninja Tune', 'PAN', 'Soul Jazz Records', 'Dekmantel',
				'R&S Records', 'Ghostly International', 'Mood Hut', 'The Trilogy Tapes', 'Hyperdub', 'Numero Group',
				'Ostgut Ton', 'Fabric', 'Erased Tapes Records', 'Analog Africa', 'Northern Electronics',
				'Light in the Attic Records', 'Workshop', 'Hessle Audio', 'Planet Mu', 'Dark Entries', 'Livity Sound',
				'Awesome Tapes From Africa', 'Soundway Records', 'Shall Not Fade', 'Running Back', 'Berceuse Heroique',
				'Kompakt', 'Rush Hour Recordings', 'Lost Palms', 'Delsin', 'Distant Hawaii', 'Trip', 'Editions Mego',
				'100% Silk', '1080p', 'Brainfeeder', 'FabricLive', 'RVNG', 'Mule Musiq', 'Sacred Bones Records',
				'Opal Tapes', 'SUED', 'Blackest Ever Black', 'Stones Throw Records', 'Houndstooth', 'Modern Love',
				'Kalahari Oyster Cult', 'Pacific Rhythm', 'Strut', 'Nyege Nyege Tapes', 'Central Processing Unit',
				'Acting Press', 'Tresor', 'Hospital Productions', 'Posh Isolation', 'West Mineral Ltd.',
				'Unknown To The Unknown', 'Planet Euphorique', 'Finders Keepers', 'PC Music', 'Regelbau', 'Studio Barnhus',
				'Banoffee Pies', 'Peoples Potential Unlimited', 'Rhythm Section International',
				'Athens Of The North', 'Coastal Haze', 'Raster-Noton', 'Hivern Discs', 'Príncipe', 'Church', 'Kranky',
				'Smithsonian Folkways', 'Silent Season', 'Safe Trip', 'Smalltown Supersound', 'Natural Sciences',
				'Honest Jon’s Records', 'Sublime Frequencies', 'Permanent Vacation', 'Semantica', 'Leaving Records', 'Mörk',
				'E-Beamz', 'All Possible Worlds', 'NAFF', 'Future Times', 'echospace', 'Antinote',
				'Melody As Truth', 'Craigie Knowes', 'Other People', 'Second Circle', 'Optimo Music', 'Magicwire',
				'Melodies International Ltd.', 'ESP Institute', 'Salt Mines', 'Klasse Wrecks', 'Peach Discs',
				'Sex Tags Mania', 'Royal Oak', 'Aus Music', 'Emotional Rescue', 'Nous\'klaer', 'Public Possession',
				'Now-Again Records', 'Sahel Sounds', 'Perlon', 'Razor N Tape', 'Timedance', 'Firecracker Recordings',
				'Astral Industries', 'Text Records', 'Tri Angle Records', 'Dial', 'Mr Bongo Records', 'Spazio Disponibile',
				'Pampa Records', 'Mobile Fidelity Sound Lab', 'DDS', 'Furthur Electronix', '4AD', 'Waxtefacts',
				'Comatonse Recordings', 'Gost Zvuk', 'Italians Do It Better', 'Habibi Funk Records', 'Butter Sessions',
				'Lobster Theremin White Label', 'Innervisions', 'Barely Breakin Even Records', 'Anjunadeep',
				'A Strangely Isolated Place', 'Invisible City Editions', 'X-Kalay', 'XL Recordings', 'Nous', 'Thrill Jockey',
				'Deep Medi Musik', 'ECM Records', 'Diagonal', 'Night Slugs', 'Dust-to-Digital', '12th Isle', 'Toy Tonics',
				'Stroboscopic Artefacts', '12k', 'Unseen Worlds', 'Shelter Press', 'Beats In Space', 'Hypnus Records',
				'Smallville Records', 'Wania', 'Room40', 'Black Truffle', 'Downwards', 'Orange Milk Records', 'Dr Banana',
				'Les Disques Bongo Joe', 'Disco Halal', 'Sound Signature', 'Sex Tags Amfibia', 'Idle Hands', 'Isle of Jura',
				'International Feel Recordings', 'Mannequin', 'EM Records', 'Acido Records', 'SLOW LIFE', 'FXHE Records',
				'Multi Culti', 'Malka Tuti', 'Freedom To Spend', 'Brownswood Recordings', 'Moonrise Hill Material',
				'Vinyl-on-demand', 'Studio !K7', 'Crème Organization', 'Help Recordings', 'Sneaker Social Club',
				'Hypercolour', 'Wisdom Teeth', 'Underground Resistance', 'Bedouin Records', 'Recollection GRM',
				'Analogical Force', 'Favorite Recordings', 'The Bunker New York', 'Scissor and Thread', 'fabric presents',
				'Sub Pop', 'Minimal Wave', 'Casa Voyager', 'Incienso', 'Avian', 'Constellation Records',
				'Touch', 'Knekelhuis', 'Bureau B', 'Sounds Of Beaubien Ouest', 'Not Not Fun Records', 'Erstwhile Records',
				'Bokeh Versions', 'Tectonic', 'DFA', 'Early Sounds Recordings', 'Smiling C', 'Gravity Graffiti',
				'World Building', 'Apron Records', 'ARTS', 'Brothers From Different Mothers', 'Drag City',
				'Growing Bin', 'Claremont 56', 'Good Morning Tapes', 'Hot Haus Recs', 'Hotflush Recordings',
				'Blue Note Records', '3024', 'Rephlex', 'Eglo Records', 'Pinkman', 'The Death Of Rave', 'iDEAL Recordings',
				'CockTail d\'Amore Music', 'Phantasy Sound', 'Subtext', 'Metalheadz', 'Umor Rex', 'Proibito', 'Arcola',
				'Sex Tags UFO', 'Offen Music', 'Spectrum Spools', 'Clone', 'Halcyon Veil', 'Die Orakel', 'Jazzman Records',
				'Monkeytown Records', 'UIQ', 'In Dust We Trust', 'Ode', 'Emotional Response', 'Efficient Space',
				'Private Persons', 'Ostinato Records', 'Hinge Finger', 'Séance Centre', 'Freakout Cult', 'Eilean Rec.',
				'Mord', 'Let\'s Play House', 'Music for Dreams', 'Phonica Records', 'Hemlock Recordings', 'Soma Records',
				'Heavenly Sweetness', 'Clone Basement Series', 'Mister Saturday Night', 'Apollo', 'Cultures Of Soul Records',
				'Going Good', 'Clone Classic Cuts', 'Don\'t Be Afraid', 'NOTON', 'Rawax', 'Brokntoys', 'Persies',
				'Who\'s Susan', 'Aniara Recordings', 'Tzadik Records', 'Discrepant', 'Denovali Records',
				'Steel City Dance Discs', 'Hospital Records', 'Africa Seven', 'Quiet Time Tapes', 'Best Record Italy',
				'Lobster Theremin Black Label', 'Sub Rosa', 'Sushitech Records', 'Cactus Traxx', 'Dansu ダンス Discs',
				'Mexican Summer', 'Another Timbre', 'Ideologic Organ', 'Mana', 'We Release Whatever The Fuck We Want Records',
				'Clone Jack For Daze', 'Ocora', 'Safe Distribution', 'Recital', 'Cititrax', 'Analogue Attic', 'Macadam Mambo',
				'City-2 St. Giga', 'Feel My Bicep', 'Superconscious Records', 'Motion Ward', 'Technicolour', 'Poly Kicks',
				'Frame Of Mind', 'Russian Torrent Versions', 'Hot Casa Records', 'Laut & Luise', 'NAAFI', 'REKIDS',
				'Echocord', 'FHUO Records', 'Latency', 'Geographic North', 'Dais Records', 'Tru Thoughts',
				'Important Records', 'Anjunabeats', 'LuckyMe', 'Black Opal', 'Cultivated Electronics', 'Home Normal',
				'Afterlife', 'Constellation Tatsu', 'Cabaret Recordings', 'Paul Institute', 'Youth', 'Friends of Friends',
				'Live At Robert Johnson', 'Janushoved', 'Life And Death', '1631 Recordings', 'Temporary Residence Limited',
				'Spectral Sound', 'Looking For Trouble', 'Lehult', 'Lobster Sleep Sequence', 'Studio Mule', 'Aiwo rec.',
				'Strictly Rhythm', 'Sähkö Recordings', 'Left Ear Records', 'Naive', 'Dolly', 'Ecstatic', 'Versatile Records',
				'FTP', 'Archives', 'Air Texture', 'DustWORLD', 'Drumcode', 'Shipwrec', 'Hakuna Kulala', 'Ultimae Records',
				'Mille Plateaux', 'Boomkat Editions', 'Exit Records', 'Seagrave', 'La Chinerie', 'Jagjaguwar', 'S.O.N.S',
				'Perc Trax', 'Samurai Music', 'Radio Nova Complete Collection', 'Berg Audio', 'Trance Wax',
				'Lullabies for Insomniacs', 'Nonesuch Records', 'Dream Catalogue', 'Local Talk', 'Island Of The Gods',
				'Mississippi Records', 'n5MD', 'Snaker Records', 'Ghost Box', 'Chain Reaction', 'Rune Grammofon',
				'Ascetic House', 'Delaphine', 'Renascence', 'Driftwood', 'International Anthem', 'Holdings Hands',
				'Mute Records', 'Cong Burn', 'P-Vine Records', 'Moving Shadow', 'oge', 'Domino', 'Bordello A Parigi',
				'Experiences Ltd.', 'Different Circles', 'Futureboogie Recordings', 'Daptone Records', 'Keysound Recordings',
				'Valby Rotary', 'For Those That Knoe', 'Frustrated Funk', 'Les Yeux Orange', 'A-TON', 'Lumberjacks In Hell',
				'Hidden Hawaii', 'Space Of Variants', 'D.KO Records', 'Duke\'s Distribution', 'Born Bad Records',
				'1Ø Pills Mate', 'Bleep', 'NDATL', 'Dance Mania', 'Token', 'Skylax', 'Fit Sound', 'BANK Records NYC',
				'Of Paradise', 'Full Pupp', 'Börft Records', 'Swamp 81', 'Glacial Movements', 'ÆX', 'Cosmic Rhythm', 'Forum',
				'Heist Recordings', 'Tempa', 'Akuphone', 'Kulør', 'Hostom', 'Klockworks', 'Correspondant', 'Eastenderz',
				'bbbbbb', 'Lustwerk Music', 'Light Sounds Dark', 'Mint Condition', 'Peacefrog Records',
				'sferic', 'Presch Media Gmbh', 'BAKK', 'Black Acre Records', 'Cin Cin', 'Cryo Chamber', 'Voyage Recordings',
				'No Hands', 'Horo', 'Axe Traxx', 'Timeisnow', 'Dixon Avenue Basement Jams', 'Nonplus Records', 'Sonic Pieces',
				'Profound Lore', 'Delusions of Grandeur', 'Kann Records', 'Ternesc', 'Wild Oats', 'Chiwax', 'Young Turks',
				'Auxiliary', 'R.A.N.D. Muzik', 'anno', 'Subwax Bcn', 'Editions Gravats', 'Kompakt Extra', 'NNA Tapes', 'Type',
				'Traumprinz', 'Irdial Discs', 'Uncanny Valley', 'Lit Level', 'Deutsche Grammophon',
				'clipp.art', 'Animals Dancing', 'Fortuna Records', 'Skam', 'Superior Viaduct', 'Arcane',
				'ava. Records', 'Kontra-Musik', 'Good Looking Records', 'Vanity Records', 'Fluid Audio',
				'Black Sweat Records', 'STRCTR', 'Endless Flight', 'Tartelet Records', 'Clone Aqualung Series',
				'Fade to Mind', 'aufnahme + wiedergabe', 'Total Stasis', '777 Recordings', 'Defected', 'Kalita Records',
				'Warriors Dance', 'Solar One Music', 'Border Community', 'Faraway Press', 'Haŵs', 'Aku', 'Luaka Bop',
				'A Colourful Storm', 'De:tuned', 'Exotic Robotics', 'L.A. Club Resource', 'Osiris Music', 'All My Thoughts',
				'Periodica Records', 'Mistress Recordings', 'Bear Family Records', 'Isla', 'ECM New Series',
				'FATi Records', 'Soave', 'Ministry Of Sound', 'Hubro', 'Penultimate Press', 'Vibraphone Records',
				'Stilla Ton', 'Field Records', 'Exotic Dance Records', 'Voam', 'Now That\'s What I Call Music!',
				'Longform Editions', 'Bassiani', 'White Material', 'Local Action', 'Greyscale', 'Marionette',
				'Echovolt Records', 'Alter', 'NIGHT SCHOOL', 'Wah Wah Wino', 'Pinkman Broken Dreams', 'Wémè Records',
				'Faitiche', 'Mechatronica', 'Teklife', 'Ken Oath Records', 'Honey Soundsystem', 'Castle Face Records',
				'Entr\'acte', 'Global Underground', 'Seilscheibenpfeiler', 'Feeding Tube Records',
				'Obscure Tape Music Of Japan', 'Metereze', 'This Is Our Time', 'Love On The Rocks', 'Mélodies Souterraines',
				'Dystopian', 'TURBO', 'Dauw', 'Diskant / DISK', 'Wolf Music Recordings', 'Viewlexx', 'Deewee',
				'Midgar Records', 'Jealous God Records', 'Udacha', 'Suara', 'Temple', 'Mojuba', 'Last Night On Earth',
				'Astrophonica', 'Grant', 'Disco Magic Melody', 'Southern Lord', 'El Paraiso Records',
				'Clone West Coast Series', 'Guidance Recordings', 'Khotin Industries', 'Transmat', 'Giegling Staub Serie',
				'[Emotional] Especial', 'Die Schachtel', 'Karlrecords', 'Past Inside the Present', 'Transatlantic',
				'On The Corner Records', 'Moderna Records', 'Captured Tracks', 'Mahogani', 'Tompkins Square',
				'This Never Happened', 'Ed Banger Records', 'UVB-76 Music', 'All City Records', 'Cómeme', 'Thesis',
				'ZamZam Sounds', 'art-aud', 'Meda Fury', 'Revoke', 'Normals Welcome', 'Themes For Great Cities', 'Withhold',
				'Born Free Records', 'Allergy Season', 'Keys Of Life', 'Novel Sound',
				'P-Vine Records - Groove Diggers Series', 'Glitterbeat', 'Super Rhythm Trax', 'KDJ', 'Good Plus', 'NON',
				'Omena', 'Jungle Gym Records', 'Morr Music', 'Secretsundaze', 'Aguirre Records', 'Fax +49-69/450464', 'VWV',
				'flau', 'SVBKVLT', 'Kye', 'Mote-Evolver', 'No Corner', 'Spacetalk Records', 'Hausu Mountain',
				'Chikyu-u Records', 'Love Notes', 'Creel Pone', 'Nu Groove Records', 'Midnight Riot', 'Joule Imprint',
				'Closer', 'Sound Stream', 'Unthank', 'Faint', 'Indigo Aera', 'Vampisoul', 'Cuneiform Records', 'YEAR0001',
				'Merge Records', 'LKR Records', 'Get Physical Music', 'BPitch Control', 'Needs - Not For Profit',
				'Djax-Up-Beats', 'NewRetroWave', 'Mysticisms', 'Botanic Minds', 'Optimo Trax', 'Purple Tape Pedigree',
				'Death Is Not The End', 'RORA', 'Crammed Discs', 'Detroit Underground', 'Heart To Heart', 'YYK no label',
				'Uniile', 'Black Jazz Records', 'Lillerne Tape Club', 'Critical Music', 'Basic Channel', 'Invisible, Inc.',
				'VOITAX', 'EEE', 'Future Classic', 'Likemind', 'No Rent Records', 'Haunter Records', 'OTOROKU',
				'Diynamic Music', 'Matador Records', 'Better Days', 'Leng Records', 'Figure', 'Crosstown Rebels',
				'Tusk Wax', 'Hokkaido Dance Club', '50Weapons', 'Canary Records', '47', 'Scuffed Recordings',
				'Origin Peoples', 'Money $ex Records', 'Hallow Ground', 'Students Of Decay',
				'Cherry Red Records', 'Deep Jungle', 'Dronarivm', 'M-Plant', '17 Steps', 'Jiaolong', 'Konstrukt',
				'Organic Analogue Records', 'Forbidden Planet', 'Bedrock Records', 'Serein', '81', 'Shimmering Moods Records',
				'Bunker Records', 'Nonplace', 'Ekster', 'Morphine Records', 'Stil vor Talent', 'Klakson', 'Digwah',
				'Orange Tree Edits', 'Rösten', 'ASL Singles Club', 'New York Haunted', 'Dirtybird', 'Höga Nord',
				'Project Mooncircle', 'Beyond Beyond is Beyond Records', 'Young Ethics', 'All Day I Dream', 'Whiteloops',
				'Phonica White', 'House Of Disco Records', 'TRULE', 'Wilson Records', 'Planet Rhythm Records',
				'Touched Music', 'Aloha Got Soul', 'GETTRAUM', 'YAPPIN', 'Strata-East Records', 'Cocoon Recordings',
				'Omnidisc', 'RFR', 'Intergraded', 'UMM', 'I Love Acid', 'Miasmah', 'Watergate Records', 'Fundamental Records',
				'1-800 Dinosaur', 'Detriti Records', 'LNS', 'Voodoo Gold', 'YYY Series',
				'World Music Library by King Records', 'Mo Wax', 'Alleviated Records', 'Tikita',
				'Something Happening, Somewhere', 'Waffles', 'Raster-Music', 'Public Release', 'Bahnsteig 23', 'Axis',
				'Planet E', 'All Caps', 'Midnight Shift Records', 'Hymen Records', 'Nervmusic Records', 'Cult Edits',
				'UN.T.O. Records', 'Dead Oceans', 'Secretly Canadian', 'Dekmantel UFO Series', 'Whiskey Disco', 'UVAR',
				'IILE', 'Staalplaat', 'Edition RZ', '5 Gate Temple', 'Non Series', 'Oscillat Music', 'Blume',
				'Eskimo Recordings', 'Les Disques De La Bretagne', 'FireScope', 'Contort Yourself', 'MCDE',
				'Sacred Rhythm Music', 'Hyperion Records', 'Ace Records UK', '20/20 LDN', 'Verdicchio Music Publishing',
				'2MR', 'Acid Test', 'Atomnation', 'Modern Obscure Music', 'Spring Theory', 'Nerang Recordings',
				'Nervous Horizon', 'Radiant Love', 'Ad Noiseam', 'Amphia',
				'Star Creature', 'Curtea Veche', 'Howl', 'Blueprint Records', 'Arma', 'Mau5trap Recordings',
				'Well Street Records', '130701', 'Monnom Black', 'Compost', 'Palto Flats', 'No Bad Days', '00:AM',
				'Cree Records', 'Wonderwheel Recordings', 'Childhood Intelligence', 'Cosmo Rhythmatic',
				'Glitterbox Recordings', 'Bruton Music', 'Traum Schallplatten', 'Marcel Dettmann Records', 'Schole Records',
				'Samurai Horo', 'Quartet Series', 'Leaf', 'Further Records', 'Libertine Records', 'Into the Light Records',
				'Atipic', 'LINE', 'Sei Es Drum', 'Phantom Island', 'Yen Records', 'Esoteric Exports', 'Ultramajic',
				'Collect-Call', 'White Peach', 'Metroplex', 'The Wire Tapper', 'Red Laser Records', 'Flightless Records',
				'Kimochi', 'Freerange', 'Les Disques De La Mort', 'Eaux', 'Tabernacle Records', 'Hippie Dance', 'Domestica',
				'Fractal Fantasy', 'Trunk Records', 'Equation Records', 'White Paddy Mountain', 'Bedrock Digital',
				'History Always Favours The Winners', 'KPM Music', 'Anthology Recordings', 'Peak Oil',
				'Funky Town Grooves', 'FTG', 'Doom Chakra Tapes', 'Ltd, W/Lbl', 'Man Band rec.', 'Tartouffe', 'INA-GRM',
				'Jj funhouse', 'hibernate', 'Rupture London', 'Far Out Recordings', 'Deeptrax Records', 'Wichelroede',
				'Worst Records', 'Nervous Records', 'M>O>S Recordings', 'Acid Avengers', 'VEYL', 'Temples of Jura', 'UNO',
				'Sound Metaphors Records', 'Med School', 'Dnuos Ytivil', 'Gost Instrument', 'Analogue Productions',
				'Neo Violence', 'Rocket Recordings', 'Discos Capablanca', '+ + + Mathematics Recordings',
				'Chopped Herring Records', 'Leisure System', 'Tripalium', 'Outer Time Inner Space', 'Mosaic', 'Rough Trade',
				'Unexplained Sounds Group', 'Wax', 'Ant-Zen', 'AM Records / Afrobotic Musicology', 'Unknown Precept',
				'Axe On Wax Records', 'Astral Spirits', 'Potatoheadz', 'Taapion Records', 'V I S', 'Clan Destine Records',
				'Clean Feed', 'DAHLIA', 'Playedby', 'Reality Used To Be A Friend Of Mine', 'Thule Records', 'Housewax',
				'There Is Love In You', 'Internasjonal', 'Exo Recordings International', 'Cosmic Bridge',
				'Poker Flat Recordings', 'Alga Marghen', 'Houseum Records', 'Z Records', 'Lux Rec', 'Ouïe',
				'Blind Jacks Journey', 'Hotline Recordings', 'Western Vinyl', 'Dalmata Daniel',
				'UNESCO Collection of Traditional Music of the World', 'Chuwanaga', 'Ancient Monarchy', 'Planet Trip',
				'Noire & Blanche', 'Soul People', 'Muscut', 'Putumayo World Music', 'Genome6.66Mbp', 'Artesian Sounds',
				'WERGO', 'Primitive', 'B.F.E Records', 'Burka For Everybody', 'Increase The Groove Records',
				'Numero Group: From The Stacks', 'Highlife', 'is / was', 'kitchen. label', 'Accidental',
				'Crimes of the future', 'IDO', 'Dirty Tapes', 'Edition Wandelweiser Records', 'Olsen', 'Puu',
				'Major Problems', 'Total Black', 'Fruit Merchant', 'Shika Shika', 'Rave Or Die', 'Green Bay Wax',
				'Ransom Note Records', 'Gobstopper Records', 'Social', 'Sour Edits', 'P.S.F. Records',
				'Golf Channel Recordings', 'Prologue', 'Bobby Donny', 'Tugboat Edits', 'RASSVET records', 'Gamm',
				'Blueberry Records', 'Bôłt', 'Cold Tear Records', 'West End Records', 'The Astral Plane', 'Whitelabrecs',
				'System Music', 'Staubgold', 'yoyaku', 'Monstercat', 'Quintessentials', 'Trost Records', 'Mirror Zone',
				'Pharaway Sounds', 'Pearson Sound', 'Pole Recordings', 'Fine', 'Version', 'Daisart', 'Helen Scarsdale Agency',
				'Balearic Blah Blah', 'Solar Phenomena', 'Good Company Records', 'DMZ', 'Punch Drunk', 'Concrete Music',
				'W.T. Records', 'Bella Union', 'Tape Throb Records', 'YoY', 'Murder Capital', 'ROHS! Records', 'Last Resort',
				'Musique Pour La Danse', 'SONOR Music Editions', 'DRG Series',
			].map(normalizeName).join('|') + ')\\b)/i',
			'color: #b4b300; font-weight: 600;'
		], [ // RED top2000 subscribed labels
			'/^(?:(?:' + [
				'13 (Silentes)', '(K-RAA-K)³', 'π', 'c-', 'Brutaż', '7K!', 'caduc.', 'How Do You Are?',
				'I\'m A Cliché', 'Ba Da Bing!', 'Roulé & Scratché', 'PRR! PRR!', 'Now That\'s What I Call \w+!',
			].map(normalizeName).join('|') + ')|(?:' + [
				'In The Red', 'Pacific City Sound Visions', 'Glistening Examples', 'Moving Furniture', 'Klanggalerie', 'XXX',
				'Fauxpas Musik', 'Pi Electronics', 'La Vida Es Un Mus', 'Archeo Recordings', 'Palace of Lights',
				'Rough House Rosie', 'C minus', 'Whyte Numbers', 'Magic Wand', 'Modal Analysis', 'Office Recordings',
				'Hard Fist', 'Afrosynth Records', 'Beat Records / Beatink', '2 Bit Crew Recordings', 'Rat Life',
				'International Chrome', 'Pressure Traxx', 'Mesh', 'Rubadub', 'Bleu Ciel', 'Frozen Reeds', 'Graded', 'Vitrine',
				'Midnight Drive', 'Hands In The Dark', 'Wex', 'Dirt Crew Recordings', 'Odion Livingstone', 'PNP',
				'Live From Earth Klub', 'Gestalt Records', 'Sound in Silence', 'Chillhop Records', 'Planet Uterus',
				'Little Axe Records', 'Ipecac Recordings', 'Let\'s Go Swimming', 'Relapse Records',
				'Pressure', 'Involve Records', 'Hard Beach Entertainment', 'The Tapeworm', 'AtomTM_Audio_Archive',
				'Reel Torque', 'Kniteforce Records', 'a.r.t.less', 'Top Dawg Entertainment', 'Ominira', 'Private Records',
				'Fuzz Club Records', 'Blank Forms Editions', 'Transversales Disques', 'YAM Records', 'Acid Waxa',
				'Suction Records', 'Mothball Record', 'ØEN REC.', 'Vanity Press Records', 'Time Released Sound',
				'Artificial Dance', 'Cajual', 'Delft', 'Secousse', 'DECISIONS', '7th Storey Projects', 'Mulen Records',
				'Editorial', 'Superfly Records', 'Philomena', 'Afro7 Records', 'Impulse! Records', 'Reclaim Your City',
				'Pulse Msc', 'Love International Recordings', 'Bitter Lake Recordings', 'Interdimensional Transmissions',
				'Tsuba Records', 'Dwig', 'Boysnoize Records', 'Djak-Up-Bitch', 'DUB', 'Traxx Underground', 'Neroli',
				'Power House', 'Wackie\'s', 'Frederiksberg Records', 'Me Me Me', 'International Deejay Gigolo Records',
				'Love Love Records', 'She Lost Kontrol', 'Cascine', 'Trikont', 'Prescription', 'Kavalanic Languages',
				'City Slang', 'Applied Rhythmic Technology', 'ART', 'Keinemusik', 'MMODEMM', 'The Flenser', 'Flenser Records',
				'Bandulu Records', 'Fever AM', 'Basic Moves', 'TGP', 'Anticon', 'ZZK Records', 'Concrete Cabin', 'Flying Nun',
				'Goon Club Allstars', 'Euromantic', 'Avenue 66', 'Saft', 'Valcrond Video', 'Fathers & Sons Productions',
				'Music Man', 'Trojan Records', 'June', 'Fina Records', 'offworldcolonies ltd.', 'Hell Yeah Recordings',
				'Nightwind Records', 'Noumenal Loom', 'Junk Yard Connections', 'Mojo Magazine', 'Medical Records LLC',
				'Geography Records', 'Mule Electronic', 'Lovely Music', 'Rinse', 'Italojohnson', 'Tropical Animals',
				'Copenhagen Underground Posse', 'Lunar Disko', 'Family Groove Records', 'Ostra Discos', 'BLKRTZ',
				'ANS', 'Truncate', 'elsewhere', 'Ancient Astronauts', 'Kitjen', 'Arsenik Records', 'Eros', 'Lagaffe Tales',
				'Clergy', 'Big Dada Recordings', 'Hundebiss', 'laaps', 'Balkan Vinyl', 'Comic Sans Records',
				'Bosconi Records', 'Out To Lunch', 'XKatedral', 'Limousine Dream', 'Innamind Recordings', 'Meakusma',
				'Deep Explorer', 'PLAY !T LOUD', 'Giallo Disco', 'Gondwana Records', 'Propaganda Moscow', 'House Crime',
				'Lone Romantic', 'Mind Records', 'Pressure Sounds', 'Korea Undok Group', 'On-U Sound', 'Super Utu',
				'Sandwell District', 'Chinabot', 'Cold Busted', 'was / is', 'Siltbreeze', 'Curle Recordings',
				'Plangent Records', 'Instinct', 'Ectotherm', '20:20 Vision', 'Ki Records', 'Tramp Records', 'Tooney Lunes',
				'Pacific Command', 'Schvitz Edits', 'Porn Wax', 'Tuff Cut', 'Ornaments', 'Infinite Machine', 'Lo Recordings',
				'Belters', 'Sofrito Records', 'Raum...musik', 'Nullpunkt', 'Dolly Deluxe', 'Northern Spy', 'Echo Echo',
				'The Final Experiment', 'Canoe', 'Seven Hills', 'Pager Records', 'Time Passages', 'Rhymesayers Entertainment',
				'Red Ember Records', 'Fleisch', 'Lost Tribe Sound', 'SUPERPANG', '22a', 'Miss You', 'Vibrant Music',
				'Empire Of Signs', 'Hippos In Tanks', 'Music Preservation Society', 'Big Doint', 'Linn Records',
				'Repitch Recordings', 'Mental Experience', 'Pear', 'Timesig', 'Deeply Rooted House', 'Groovence',
				'Transatlantyk', 'Needwant', 'SlothBoogie', 'Third Ear Recordings', 'Pont Neuf Records', 'Hart & Tief',
				'Return To Disorder', 'Slip', 'Skudge Records', 'Carpe Sonum Records', 'GASP', 'Lazare Hoche Records',
				'Better Listen Records', 'Distant Worlds', 'Trelik', 'Pessimist Productions', 'Voodoo Funk',
				'Rising High Records', 'Time To Express', 'Late Night Burners', 'Dynamic Reflection', 'Bottom Line Records',
				'Lovers Rock', '1985 Music', 'Monsieur Blue', 'La Beauté Du Négatif', 'Mood Of The Era',
				'Asthmatic Kitty Records', 'THEM', 'Sonic Groove', 'Ultra Eczema', 'Aline Brooklyn', 'Moustache Records',
				'Mosaic Records', 'Pets Recordings', 'Past Due', 'Distant Horizons', 'Why So Series', 'Ellum Audio',
				'Backatcha Records', 'Blorp', 'Audio Fidelity', 'Svek', 'Low Income $quad', 'Kynant Records', 'UGONGETIT',
				'Sound Process', 'Club In Theory', 'Diskotopia', 'Ombra International', 'Odd Even', 'Power Vacuum',
				'Imprints Records', 'Dinzu Artefacts', 'HAUS of ALTR', 'Natura Sonoris', 'Iron Lung Records', 'Gizeh Records',
				'AP Musik', '-ous', 'Werk Discs', 'Bad Manners Records', 'Roots For Bloom', 'Jamwax', 'Reinforced Records',
				'Infuse', 'Seven Villas', 'Meander', 'Modern Cathedrals', 'Fa>Ie Records', 'Anòmia', 'Transcendent', 'TZU',
				'Radio Martiko', 'Tooloop', 'Panzerkreuz', 'Crowdspacer', 'New Flesh', 'Holuzam', 'Counter 99',
				'Pleasure Zone', 'Splice Sample Packs', 'Matsuli Music', 'Buda Musique', 'Frémeaux & Associés', 'St. GIGA',
				'Mechatronica White', 'Ghost Phone', 'Village Green', 'OOH-sounds', 'Not an Animal Records', 'Heisenberg',
				'Dement3d Records', 'Instant Classic', 'Goaty Tapes / House Rules', 'Black Focus Records', 'Season of Mist',
				'Occult Research', 'W&O Street Tracks', 'Shogun Audio', 'Sargent House', 'Lost & Found',
				'Dream Raw Recordings', 'Fokuz Recordings', 'Selva Discos', 'Sublime Records', 'Deep Sound Channel',
				'Headstrong Records', 'Genot Centre', 'Legwork', 'BodyParts Vinyl', 'Wicked Bass', 'TerraFirm',
				'Speculations Editions', 'I, Voidhanger Records', 'Document Records', 'Disco Hamam',
				'Numero Group: Eccentric Soul', 'Western Lore', 'My Love Is Underground', 'Dischi Autunno', 'Nowt Recordings',
				'Hot Creations', 'Astralwerks', 'MOi', 'Forgotten Future', 'Jazzy Couscous', 'AC Records', 'Eerie',
				'Shaw Cuts', 'Soul Brother Records', 'Annulled Music', 'Electrix Records', 'a friend in need', 'Happy Skull',
				'Rapid Eye Movement', 'Playstation Labs', 'Tone Glow Records', 'Pelvis Records', 'Lowless Music',
				'Electric Deluxe', 'Affin LTD', 'Fania Records', 'Feines Tier', 'Silk Music', 'High Jazz* Records', 'Profan',
				'City Centre Offices', 'Waving Hands Records', 'Fat Possum Records', 'Owsla', 'Finale Sessions',
				'Is It Balearic? Recordings', 'Dog Show Records', 'Baum Records', 'The Nothing Special', 'Relief Records',
				'Running Back Incantations', 'Unterton', 'Extreme', 'Music Matters', 'Nuxxe', 'Yerevan Tapes',
				'Harmonia Mundi', 'Atavisme', 'BIS Records', '4 To The Floor Records', 'Root Strata', 'Thank You', 'Vanila',
				'Diffuse Reality', 'Love Potion', 'Inner Balance', 'Yoshitoshi Recordings', 'Folklore Tapes', 'Riot Season',
				'Teranga Beat', 'PRS', 'Cure Music', '31 Records', 'Ormolycka', 'Dragon\'s Eye Recordings', 'Dance Around 88',
				'20 Buck Spin', 'Sucata Tapes', 'Flaming Pines', 'Strangelove Music', 'Dub Store Records', 'Be With Records',
				'Kontakt Records', 'Mystic Versions', 'Autonomous Africa', 'Moon Glyph', 'NG Records', 'Nu Guinea Records',
				'Greta Cottage Workshop', 'Freund Der Familie', 'Enchufada', 'Accents Records', 'BeauMonde Records',
				'Light Of Other Days', 'Black Orpheus', 'Fool\'s Gold Records', 'Knives',
				'Public System Recordings', 'Kindisch', 'Paranoid London Records', 'Asquith', 'Numero Group: Main Series',
				'Vakant', 'Empreintes DIGITALes', 'Cardinal Fuzz Records', 'Source Records', 'Because Music',
				'Delicate Wash', 'Luv N\' Haight', 'Squirrels On Film', 'E-MISSIONS', '~scape', 'De Stijl', 'Artefakt',
				'OUTPLAY', 'Vade Mecum', 'Nilla', 'Woe To The Septic Heart', 'Ritual Poison', 'Pluto', 'Baran Records',
				'Riotvan', 'Underground Quality', 'Beer On The Rug', 'falling apart', 'Light Touches Records',
				'My Own Jupiter', 'Software', 'Ten Thousand Yen', 'SUBTIL RECORDS', 'Owl', 'kaos', 'QNQN', 'Will & Ink',
				'IIKKI', 'Third Man Records', 'In Any Case Records', 'Petrola 80', 'Minibar', 'Box Aus Holz', 'Ranges',
				'Tropical Disco Records', 'mindcolormusic', 'Pryda Recordings', 'Sistrum Recordings',
				'Telum', 'em:t', 'Emit', 'FatCat Records', 'Tief Music', 'Rhino Box Sets', 'Politics Of Dancing Records',
				'Toolroom Records', 'Ukiyo', 'The ECM Collage Sorted by Release Date', 'Red Planet',
				'Moog Recordings Library', 'Night Time Stories', 'Tone Dropout', 'Dewtone Recordings',
				'Future Sounds Of Jazz', '99CHANTS', 'Evigt Mörker', 'RidingEasy Records', 'Fire Records',
				'Bass Culture Records', 'Lunar Orbiter Program', 'Joyful Noise Recordings', 'godmode music',
				'Varèse Sarabande', 'Don Giovanni Records', 'Dolly Dubs', 'Daymare Recordings', 'Small Hours',
				'Subliminal Sounds', 'European Carryall', 'Madhouse Records', 'Bitta', 'Peder Mannerfelt',
				'Numero Group 5000 Line', 'Nachtbraker', 'Slow Motion Records', 'Kent Records', 'Suol', 'Nuclear Blast',
				'Chocolate Monk', 'Bedroom Community', 'Invitation', 'T4T LUV NRG', 'Clave House', 'Monaberry',
				'9300 Records', 'TRAM Planet Records', 'KUMP', 'Samurai Red Seal', 'AWRY', 'ara',
				'Circular Limited', 'Soundscape Versions', 'Suezan Studio', 'Inner Islands', 'Bite', 'Club Lonely',
				'Drumpoet Community', 'Liquicity Records', 'Horn Wax', 'Edits From', 'Mad Decent', 'Mastercuts',
				'Stockhausen-Verlag', 'Visionquest', 'Svart Records', 'Danza Tribale', 'Jump Source', 'Rebirth', 'Plus 8',
				'Rocksteady Disco', 'Modularz', 'Carpark Records', 'Warm Up Recordings', 'Moods & Grooves',
				'The Nite Owl Diner', 'YOYAKUZA', 'Super Tuff Records', 'Hooj Choons', 'Portraits GRM',
				'Dimensions Recordings', 'Jheri Tracks', 'Disco Segreta', 'NES', 'The Lauren Bacall', 'Submerge',
				'Touched Electronix', 'Novamute', 'Dog In The Night Records', '7777', 'Polyvinyl Record Company',
				'Nonesuch Explorer Series', 'Scenario', 'Anagram', 'Perfect Aesthetics', 'Brenda', 'Innovative Communication',
				'Breakin\' Records', 'An|dromeda', 'Topshelf Records', 'Förlag För Fri Musik', 'MixCult', 'ShellacHead',
				'Hardworksoftdrink', 'Bright Sounds', 'Portals Editions', 'First Second Label', 'Shitkatapult',
				'Creole Stream Music', 'Cadenza', 'Bootleg Tapes', 'Hoarder', 'Cabinet Records', 'Common Thread', 'Móatún 7',
				'Bokhari', 'Griselda Records', 'Apparel Music', 'Answer Code Request', 'Shadoks Music', 'Float Records',
				'Les InRocKuptibles', 'Roche Musique', 'Chondritic Sound', 'KMS', 'Ville Nouvelle', 'Vlek', 'Gruenrekorder',
				'Nation', 'Kindred Spirits', 'Meanwhile', 'Art-E-Fax', 'Black Editions', 'Secret Squirrel', 'Mixed Signals',
				'Sportiv', '030303', 'Improvised Music from Japan', 'TRUST', 'BGP Records', 'A Made Up Sound',
				'One Little Independent / One Little Indian', 'Guerssen', 'R-Zone', 'NWAQ', 'Mistry', 'Constant Sound',
				'Queeste', 'Garden of Delights', 'Lost in Luise', 'De Lichting', 'Infinity Trax Records',
				'Edit Select Records', 'Groovement', 'My Favorite Robot Records', '90\'s Wax', 'Maeve', 'Lüüd Discs',
				'Paling Trax', 'Skudge White', 'Bossmusik', 'Rutilance Recordings', 'SATURATE!RECORDS', 'Utech Records',
				'Her Records', 'Gated', 'Instruments of Discipline', 'The Omni Recording Corporation', 'Katermukke',
				'Tehnofonika Records', 'Concentric Circles', 'Luxury Service Records', 'Gost Archive',
				'Les Disques Du Crépuscule', 'Circus Company', 'Aspecto Humano', 'Steyoyoke', 'Skire', 'Blacklist',
				'Mixpak Records', 'Klammklang', 'Code Is Law', 'EXperimental', 'Star Dub', 'Dischord Records',
				'Centre Neptune', 'Colemine Records', 'Hat Hut Records', 'Mayak', 'Power Station', 'Gravitational Waves',
				'Apollonia', 'GALAXIID', 'Three Blind Mice', 'Four Flies Records', 'Nocta Numerica Records',
				'Body Parts Records / BP Digital / BodyParts Vinyl / BP MindSeries', 'Dream Ticket', '4 Lux',
				'Memory Remains', 'Rotten City Files', 'Utter', 'Lapsus Records', 'Elektrolux', 'Bastakiya Tapes',
				'Polen', 'House of Mythology', 'Best Effort', 'Lumbago', 'Blaq Numbers', 'Japan Blues', 'Diverse System',
				'Kyoku Records', 'Saint Marie Records', 'Perpetual Rhythms', 'Charlois', 'Archipel', 'Hed Kandi',
				'Happiness Therapy', 'Blue Hour', 'Twin Turbo', 'Barba Records', 'AcidWorx', 'Mego', 'Kommuna Tapes',
				'Bastard Jazz', '/\\Aught', 'Lontano Series', 'M_nus', 'Minus', 'La Scie Dorée', 'Luck Of Access', 'Ansia',
				'Sound Pellegrino', 'Guruguru Brain', 'Neubau', 'Where We Met', 'Moment Records', 'Falling Ethics',
				'Endotherm', 'New York Trax', 'Discotexas', 'Nyami Nyami Records', 'Rotterdam Electronix',
				'Institut for Dansk Lydarkæologi', 'Institute for Danish Sound Archaeology', 'Touch From A Distance',
				'Recording', 'Redseal Records', 'Æ Recordings', 'HVNX', 'The Brane', 'Showboat Masterpiece Collection',
				'World Of Paint', 'Pure Space', '...txt', 'Playhouse', 'X/OZ', 'TAR', 'Stamp Records', 'Throne Of Blood',
				'Jens Records', 'Wewantsounds', 'Dirtybird Select', '2000 Black', 'Dotei Records',
				'Unfathomless', 'Eevo Lute Muzique', 'Muzan Editions', 'Deathwish Inc.', 'Calypso Records',
				'Off Minor Recordings', 'Ovum Recordings', 'LF RMX', 'Ziemia', 'Luminelle Recordings', 'Woodsist',
				'Rain&Shine', 'Centre Source Records', 'Magazine', 'Future Nuggets', 'Butter Side Up',
				'Virgin Babylon Records', 'Portage Garage Sounds', 'Earth Plates', 'Scientific Wax', 'Zabte Sote',
				'Windham Hill Records', 'Blackloops', 'Enfant Terrible / Gooiland Elektro', 'New World Records',
				'X-Ray Records', 'Weird World', 'Leyla Records', 'La Vida', 'Chronological Classics', 'Vintage Voudou',
				'Regraded', 'Hanson Records', 'Urashima', 'Greco-Roman', 'Lyssna Records', 'Research Records', 'ESP-Disk',
				'Paradise Of Bachelors', 'Reflective', 'Attic Music', 'FIDES', 'WNCL Recordings', 'Down By The Lake',
				'Harthouse', 'Kompakt Pop Ambient', 'Oscillate Tracks', 'Notes On A Journey', 'Something', 'Alpha Classics',
				'Oblique Music', 'SITU Records', 'Riverette', 'Soundman Chronicles', 'Tanum', 'ZYX Records', 'Eudemonia',
				'Tochnit Aleph', 'Childhood Electronix', 'Partisan', 'RAM Records', 'Les Points', 'Sintope Vinyl Series',
				'Coyote Records', 'Cease & Desist', 'Weevil Neighbourhood', 'Basic Fingers', 'Housecraft', 'Homage',
				'Names You Can Trust', 'Brew', 'Lex Records', 'P&D', 'Studio One', '1432 R', 'Atangana Records', 'Kvitnu',
				'Tech Itch Recordings', 'Disciples', 'Fantasy Love Records', 'Balkan Recordings', 'Flyance', 'Shtum',
				'Takt Jazz Series', 'Cold Spring', 'Chill Mountain Rec', 'Fuse London', 'Sound Of Vast', 'Editor\'s Kutz',
				'(JK Broadrick\'s) Avalanche Recordings', 'Subwax Excursions', 'Desolat', 'Girada Unlimited',
				'Lion Productions', 'Interactive Test', 'Shanti Radio Moscow', 'Oscilla Sound', 'Referenz',
				'Future Vision Records', 'Red Motorbike', 'Shukai', 'G.O.O.D. MUSIC', 'La Casa Tropical',
				'Index Marcel Fengler', 'Saravah', 'Bio Rhythm', 'Elephant Moon', 'Gravity Wave', 'Strength Music Recordings',
				'Eternell', 'South London Analogue Material', 'Consumer Waste', 'Incidental Music', 'Stowaway',
				'ANTI- Records', 'Grade 10', 'Lokomotiv', 'Nocturne Edits', 'Rounder Records', 'Discomatin', 'Rural Colours',
				'Mordant Music', 'Dynamic Tension Records', 'Shadow City', 'West Friends',
				'Numero Group: Cabinet of Curiosities', 'Série Limitée', 'Killekill', 'Plank Records', 'Phormix Records',
				'Be As One Imprint', 'Slam City Jams', 'Gilead Media', 'Sudbeat Music', 'Mobilee Records', 'Rue De Plaisance',
				'Duppy Gun Productions', 'Utopia Records', 'Rhizome.s', 'Janus Berlin', 'Spearhead Records',
				'Olde English Spelling Bee', 'Mushroom Hour Half Hour', 'Third Try Records', 'Hitzone', 'Vis Rev Set',
				'Palinoia', 'Counter Records', 'Connaisseur Recordings', 'Circadian Rhythms', 'General Purpose',
				'Bror Records', 'Rumors', 'Salsoul Records', 'Roam Recordings', 'Tympanik Audio', 'DIVISI62',
				'Little Helpers', 'Trouble In Mind Records', 'F Communications', 'Mountain Explosion Device',
				'Houses In Motion', 'Breaker Breaker', 'Orion Records', 'Impulsive Habitat', 'Polity Record', 'Balance',
				'430 West', 'Time Capsule', 'HAVEN', 'The Corner', 'Schematic', 'Infraction', 'Interchill Records',
				'Parquet Recordings', 'Stilleben Records', 'Minimood', 'Exit Strategy', 'In Paradisum', 'Crevette Records',
				'Edits Du Plaisir', 'Ada Kaleh Romania', 'Echocentric Records', 'Tvir', 'R - Label Group', 're:st', 'Environ',
				'Filth on Acid', 'Selador Recordings', 'Moran Music Group', 'Vertv Records',
				'Sympathy For The Record Industry', 'Blind Vision Records', 'Redscale', 'MIE Music', 'NEOS', 'Siamese', 'VHF',
				'LTM Recordings', 'FFRR', 'Full Frequency Range Recordings', 'Saga', 'Track Mode', 'Micronesia',
				'Thinner Groove', 'Rhino', 'The Very Polish Cut-Outs', 'Waterpark', 'Trax Records', 'Zeitnot', 'Tomahawk',
				'Hush Hush', 'Kabalion Records', 'DEEK Recordings', 'Drumma Records', 'Yazoo', 'Black Bones',
				'Biologic Records', 'HotMix', 'EE Tapes', 'K Records', 'Wolfskuil Records', 'LSD - Liquid Sound Design',
				'Loma Vista', 'Karaoke Kalk', 'Drop Bass Network', 'Duro', 'Suburban Base Records', 'Prime Numbers',
				'Pomme Frite', 'Eotrax', 'Sonar Kollektiv', 'Triassic Tusk Records', 'Polar Seas Recordings', 'Paraiso',
				'SIGE Records', 'Deep Frontier Records', 'Banana Hill', 'Mouseville', 'Vivrant', 'Table Of The Elements',
				'Passport To Paradise', 'Beats Of No Nation', 'Shamandrum',
				'Robsoul Recordings', 'microCastle', 'Parachute Records', 'Edizioni Mondo', 'PUPPY TAPES',
				'King Records', 'TEMƎT Music', 'Twig',
			].map(normalizeName).join('|') + ')\\b)/i',
			'color: #8bb400; font-weight: 600;'
		], [ // RED top3000 subscribed labels
			'/^(?:(?:' + [
				'candomblé', 'Audio. Visuals. Atmosphere.', 'No.', 'Мелодия', 'acmé', 'C.A.N.V.A.S.', '3000°', 'Frendzone!',
				'Stratford Ct.', 'Phoenix G.', 'Who\'s Afraid Of 138?!', 'Galakthorrö', 'complet.','Who, Whom?', 'prisma.',
				'Jumpin\' & Pumpin\'',
			].map(normalizeName).join('|') + ')|(?:' + [
				'Gruuv', 'Exploited', 'Altered Moods Recordings', 'Mongo Fett', 'Monte Cristo', 'Karlovak', 'Factory Records',
				'Binemusic', 'Family Vineyard', 'Forced Nostalgia', 'Puss', 'Violent Cases Records', 'Dream Of Dystopia',
				'Rotters Golf Club', 'Cyclic Law', 'MUJA', 'Bar25 Music', 'Irradial', 'Club No-No', 'Eye For An Eye',
				'Mental Groove Records', 'Cooking Vinyl', 'Kairos', 'TAL', 'Supercinema Records', 'Daydream France', 'Inedit',
				'Shaded Explorations', 'Irenic', 'Daytrotter', 'mappa', 'SunSeaSky', 'People Must Jam', 'Feelings Worldwide',
				'Madlib Invazion', 'Cantaloupe Music', 'Harbinger Sound', 'MM Discos', 'Against Fascism Trax', 'blundar',
				'Warehouse Rave', 'Transmigration', 'Raw Culture', 'Khemia Records', 'W.25TH', 'Hive Mind',
				'Death Waltz Recording Company', 'Dark Descent Records', 'Half Baked', 'Sol Selectas', 'Utique', 'Deep Cover',
				'Cosmic Chronic', 'Budget Cuts', 'Yoruba Records', 'The Magic Movement', 'Kaer\'Uiks', 'Henry Street Music',
				'BoxedLDN', 'Strange Rules', 'Jahtari', 'Celestial harmonies', 'Fnac Dance Music Division', 'UFO Inc.',
				'Projekts', 'Farbwechsel', 'Cool Ranch', 'Lurid Music', 'Oaks', 'MethLab', 'Nein Records', 'Tamed Musiq',
				'Waehlscheibe', 'Lick My Deck', 'Common Edit', 'Sounds & Sequences', 'Clubhouse Records', 'Alien Jams',
				'Futuribile', 'Olympos', 'Diamond Life', 'Cache Cache', 'Linear Movement', 'Def Jam Recordings',
				'The Other Planet', 'Fresh 86', 'Strictly Jaz Unit Muzic', 'Horoom', 'TH Tar Hallow', 'DiY Discs',
				'Nail Shop Records', 'Meandyou', 'Hardcore Underground', 'La-La Land Records', 'Engrave Limited', 'Philophon',
				'Biophon Records', 'Preserved Sound', 'Jalapeno Records', 'Shall Not Fade White', 'Film',
				'Johns Kingdom', 'RRRecords', 'D-Mood Records', 'Senso Sounds', 'Vinyl Underground', 'Galdoors', 'Homesick',
				'Phantom Limb', 'Resin', 'U-Udios', 'Fate and Fiction Recordings', 'Day By Day', 'Sounds Of The Dawn',
				'Kingdoms', 'ESOTERIC', 'Data Discs', 'Sundazed Music', 'Boring Machines', 'Nummer',
				'Death & Leisure', 'Green', 'Alien Passengers', 'Activia Benz',
				'Ici d\'ailleurs', 'Afrodisia Records Limited', 'MUSIC FOR THE OTHER PEOPLE PLACE', 'STAMP Records',
				'Kashual Plastik', 'The Mood Mosaic', 'Highwood Recordings', 'Imbalance Computer Music',
				'Sleazy Beats Recordings', 'Tape Records Amsterdam', 'Horizontal Ground', 'Artikal Music UK',
				'Terrestrial Funk', 'House Plants Records', 'Cold Recordings', 'Midnight Themes', 'Holger', 'Hobo Camp',
				'Stockholm LTD', 'Pulse Drift', 'D3 Elements', 'Dub On Arrival', 'KUNSTKOPF', 'Edições CN',
				'Forget The Clock', 'India Navigation', 'Jacktone Records', 'DFC', 'Paper Recordings', 'Sounds Delicious',
				'Vortex Traks', 'Beachcoma', 'Burek', 'Persephonic Sirens', 'Leleka', 'IDS RECORDINGS', 'Crónica', 'Aeon',
				'Lumière Noire', 'Uzuri', 'WADA', 'Boomarm Nation', 'House Puff', 'Work Them Records', 'Souvenir',
				'Kanja Records', 'Industrial Techno United', 'Fuselab', 'Som Livre', 'abartik', 'Recordeep', 'DNH Records',
				'Edit Service', 'Emotions Electric', 'Neurot Recordings', 'Opilec Music', 'Khaliphonic', 'Argot',
				'NI UN PERO', 'Jongno Edits', 'Club Chai', 'Fragrant Harbour', 'Bass Agenda Recordings', 'Rhythm & Sound',
				'Moscow Records', 'Winds Measure Recordings', 'Clay Pipe Music', 'Black Pearl Records',
				'Seventh Sign Recordings', 'Aspect Music', 'Frozen Border', 'Les Indispensables de Diapason',
				'Natural Selections', 'Automatic Writing', 'Tom & Jerry', 'Deathbomb Arc', 'UKF Music', 'Mello Music Group',
				'Skull Disco', 'Kikimora Tapes', 'Crash Symbols', 'Earwiggle', 'Unrelatable', 'Gloo', 'Enclave Records',
				'Danse Noire', 'Spekk', 'Pace Yourself', 'New Electronica', 'Infrastructure New York',
				'Big Break Records', 'BBR', 'Altered Sense', 'Faut Section', 'Pryda Friends', 'Soul:R', 'Les Edits Du Golem',
				'Ubiquity', 'Kern', 'Silent', 'Alpha Pup Records', 'Fake Society', 'BubbleTease Communications', 'Phinery',
				'Pressure Traxx Silver Series', 'Think! Records', 'Digitalized Planet B', 'RAL Series', 'Shahr Farang',
				'Alchemy Records', 'Mode Records', 'Bitterfeld', 'Rave Alert', 'Absurd TRAX', 'Varvet',
				'Heimat Der Katastrophe', 'Armada Music', 'Ahrpe Records', 'Tzinah Records', 'Chapter Music', 'Eternal Ocean',
				'Kolour LTD', 'Classic Music Company', 'Tonal Unity', 'Three Lobed Recordings', 'Innovative Leisure',
				'Sublunar', 'NYC Loft Trax', 'Liberation Technologies', 'Lit City Trax', 'Palilalia Records',
				'Intakt Records', 'World Circuit', 'Salviatek', 'Wah Wah 45s', 'Rüf Kutz', 'Elektrohasch Records',
				'Lofi Records', 'ChilledCow Records', 'Thug Records', 'The Healing Company', 'New Amsterdam Records',
				'Burger Records', 'Noble', 'Only Ruins', 'Esoteric Recordings', 'It\'s Not A Genre It\'s A Feeling',
				'Fasten Musique', 'Avant Records', 'Stablo', 'High Focus Records', 'Simple Music Experience',
				'Objects Limited', 'Ownlife', 'Heavy Psych Sounds Records', 'ACT Music',
				'General Production Recordings', 'GPR', 'Secret Society Chile', 'Lucky Spin Recordings', 'Sono Luminus',
				'PXWHITE', 'Exalt Records', 'PH17', 'bruit direct disques', 'r.hitect', 'Knotweed Records', 'Ferox Records',
				'Arts & Crafts', 'ReR Megacorp', 'Suntrip Records', 'Afterhouse', 'Duca Bianco', 'Spalax Music',
				'The Phantasy', 'Amotik', 'Pretty Sneaky', 'Point Of View', 'Loft Records', 'These Eyes',
				'Grapefruit Records', 'Tronic', 'SEQUALOG', 'Beste Modus', 'Klang Elektronik', 'Repsies', 'Rett I Fletta',
				'Mountain People', 'Azure Vista Records', 'Art Into Life', 'Precious Materials', 'V/Vm Test Records',
				'A-Musik', 'Touch and Go Records', 'Hyped To Death', 'DGTL Records', 'Just This', 'Diggin\' Disco Deep',
				'Intrigue Records', 'French Press Lounge', 'Kill Rock Stars', 'Zenon Records', 'Intellitronic Bubble',
				'Acid Camp Records', 'Beatservice Records', 'End Of Perception', 'Times Are Ruff', 'Zhark Recordings',
				'New Kanada', 'Lett Records', 'Shanti Planti', 'Dubwax', 'Banlieue', 'Blood Music', 'OCP', 'Fossil Archive',
				'Tessellate', 'Nostilevo Records', 'Handstitched* Recordings', 'miNIMMAl movement', 'Matrix',
				'Eye Deep Leez Recordings', 'Lifetones', 'Futuresequence', 'Sofa', 'Institut du Monde Arabe',
				'Fasaan Recordings', 'EWax', 'VAKNAR', 'Nduja', 'OnderStroom Records', 'Mother\'s Finest', 'Fringe White',
				'VOY Records', 'MotorCity Wine Recordings', 'Modeight', 'Amadeus Records', 'Outhere label group',
				'Sulta Selects Silver Service', 'Mango Alley Recordings', 'Trimurti', 'Frotee', 'Paradigm Discs',
				'Deep Sea Frequency', 'Tau', 'Rocafort Records', 'International Black', 'Parabel', 'SKIN GRAFT records',
				'BleeD', 'Live Ones', 'Avon Terror Corps', 'Vaerel Records', 'Green Bay Black Label Series', 'Dote',
				'Of Unsound Mind', 'Epitaph Records', 'Céad', 'Boogie Box', 'Reading Group', 'Dust Science Recordings',
				'Insurgentes', 'MoBlack', 'SHUT', 'Vision Recordings', 'Fat Wreck Chords', 'Parallel Minds',
				'Freude Am Tanzen', 'Parkway Records', 'Slowcraft Records', 'Ahead of our time', 'Infinite Pleasure',
				'Empty Editions', 'Col Legno', 'Ván Records', 'Shake', 'Nuclear War Now! Productions',
				'Fragile Records', 'Loci Records', 'Regional Bears', 'Flexxseal', 'Orange Mountain Music', 'Sunklo',
				'DKA Records', 'Finest Hour Records', 'Strange Life Records', 'Skudge Presents', 'Saved Records',
				'Rush Hour Store Japan', 'Lavalava Records', 'Material Image', 'Siren Records', 'Takuroku', 'Twisted Records',
				'Emotive Records', 'Soleil Zeuhl', 'La souterraine', 'Childisc', 'Palicavonzvreca', 'Lifestyle',
				'The Stone Tapes', 'Telephone Explosion Records', 'Dext Recordings', 'VDSQ', 'Cold Meat Industry',
				'Boy Better Know', 'Global Skywatch', 'Herzblut', 'Confused House', 'Zyntax Motorcity', 'Slaapwel Records',
				'Conditional', 'Old Europa Cafe', 'Polytechnic Youth', 'Bellissima! Records', 'Voyage Direct',
				'Wolf + Lamb Music', 'Melodiya', 'Hungry Music', 'Pre-Cert Home Entertainment', 'Kapsize',
				'We Release Jazz', 'Direct Beat', 'Piff Records', 'I/Y', 'Granulart Recordings', 'Night Tide',
				'Between Places', 'Vienna', 'Color Disc / Tapes', 'On Loop', 'whatmusic.com', 'Sameheads', 'Candela Rising',
				'Dub Disco', 'Funnuvojere', 'Shango Records', 'Steyoyoke Black', 'Fuck Reality', 'Eclipser Chaser',
				'Glasgow Underground', 'NCA', 'Boogie Times Records', 'Amam', 'Jolly Jams', 'Objekt', 'MYOR',
				'All Saints Records', 'ManMakeMusic', 'Metropolis', 'Vivod', 'Idle Press', 'Oye Edits', 'Blank Mind', 'Nona',
				'Puzzlebox Records', 'Ouïe Circle', 'A14', 'Soulection', 'Malin Genie Music', 'Second Sleep', 'Nami',
				'kill the dj', 'Symbols Recordings', 'Pariter', 'releisure', 'Terrorhythm Recordings', 'SNTS',
				'Works The Long Nights', 'LDNWHT', 'Flexi Cuts', 'Real World Records', 'DCC Compact Classics',
				'Stickman Records', 'Intervallo', 'Second Editions', 'Moodmusic', 'Disco Deviance',
				'KX', 'Mystic & Quantum', 'The Complete Motown Singles', 'Dailysession Records',
				'Details Sound', 'Marston Records', 'Le-Edits Records', 'Over/Shadow', 'Jack Trax',
				'Most Excellent Unlimited', 'These Things Take Time', 'Arcing Seas', 'GHE20G0TH1K', 'Nø Førmat',
				'Superior Elevation Records', 'Junior Boy\'s Own', 'Peng Sound', 'Certain Circles', 'Art Mechanical',
				'Leo Records', 'Kontor Records', 'Formation Records', 'Meta', 'Serious Grooves', 'Discothèque Fantastique',
				'Acroplane Recordings', 'T&W Records', 'Sarah Records', 'Stern\'s Music', 'Step Ball Chain',
				'Soul Revolution', 'Young Adults Recordings', 'WARNING', 'Ann Aimee', 'We Play House Recordings',
				'Emphasis Recordings', 'Atlantik Wall Records', 'Millions Of Moments', 'Ornate Music', 'Exile', 'Ndagga',
				'Happy Records', 'Pressure Makes Diamonds', 'Mind Dance', 'Adult Only', 'Paralaxe Editions', 'Norite',
				'World Village', 'Sun Ark Records', 'Partyfine', 'Secret Studio Records', 'Life Is For Living',
				'Orchid Tapes', 'Manjumasi', 'Abstrakce Records', 'Zoharum', 'Plancha', 'Bánh Mì Verlag', 'Radio Matrix',
				'Zone', 'Blood And Fire', 'Einmusika Recordings', 'Welter Records', 'Crow Castle Cuts',
				'ATLANT Recordings', 'Transfigured Time', 'Brunette Editions', 'Plastik People Recordings', 'deepArtSounds',
				'Neo Ouija', 'Grabaciones Accidentales', 'Organic-Music', 'HouseOnWax', 'Sundance', 'Flexout Audio', 'UZVAR',
				'Take It Easy', 'Orbeatize', 'Passat Continu', 'Wagram', 'Machine Jazz', 'Opia Records', 'Trick',
				'Octopus Records', 'Climate of Fear', 'Clarity Recordings', 'Rebellion', 'Earth Recordings', 'COD3 QR',
				'Left Hand Path', 'Midibasics', 'True Romance', 'De Stijl', 'Closer To Truth', 'Ohm Resistance',
				'Had To Do It', 'Slices Of Life', 'Marmo Music', 'Open Hand Real Flames', 'Jack Dept', 'Präsens Editionen',
				'Schenkelspreizer', 'Masterworks Music', 'Sentry Records', 'Fachwerk', 'Muzique Records', 'Network Medien',
				'Artetetra', 'Knee Deep In Sound', 'Saltern', 'Hesperian Sound Division', 'Potlatch', 'Lionoil Industries',
				'Prophecy Productions', 'Chit Chat Records', 'Akarma', 'Renraku', 'Balloon & Needle', 'Transonic',
				'Luettje Luise', 'Metatone', 'Failsafe', 'Mutual Dreaming', 'Eklo', 'Dusty Groove',
				'Heliocentric Music', 'True Panther Sounds', 'Capodopere', 'Chopshop', 'Harmony Rec.', 'Wandering Eye',
				'Delicacies', 'Special Forces', 'Sending Orbs', 'East End Dubs Vinyl', 'OKBRON', '2 B REAL',
				'Fantastic Friends Recordings', 'Kwench Records', 'Noise Manifesto', 'Shelter Records', 'Melliflow',
				'Copie Blanche', 'Numero Group 1200 Line', 'Preservation', 'Tsugi Sampler', 'Manitou', 'Photic Fields',
				'Numero Group: Numerophon', 'Numero Group: Numbero', 'Saddle Creek Records', 'WSNWG', 'CGI Records',
				'All Inn Black', 'Spinnin\' Records', 'Wave Music', 'Coloursound Library', 'Thirty Year Records',
				'From Another Mind', 'AVANT! Records', 'Point G', 'Computer Club', 'Gomma', 'L.B. Produce',
				'Second Hand Records', 'Noir', 'ByrdOut', 'Bopside Records', 'Release Sustain', 'Vinyl Me Please',
				'International Sun-Earth Explorer', 'MindTrip Music', 'Truesoul', 'Fayer', 'Slumberland Records',
				'Place No Blame', 'Lanthan.audio', 'MBG International Records', 'Feel International', 'Irma',
				'La Bestiole Records', 'Smoke Machine', 'Gravitational', 'Eye Q Records', 'Sobriquet Records',
				'Ritual Acquaintance', 'Lemongrassmusic', 'JMG Recordings', 'Huinali Recordings', 'Schnitzel Cuts',
				'Mireia Records', 'PTG Records', 'Oslated', 'Cortizona', 'Center 91', 'Myor Massiv', 'Fuego International',
				'Public Information', 'Oil Gang', 'three:four records', 'Glossa', 'Zehnin', 'もしもし', 'Moshi Moshi Records',
				'Wrong Era', 'Notte Brigante', 'Sulta Selects', 'Chandos', 'Tupiar Records', 'Blossoming Noise', 'Crue',
				'ProgQuébéc', 'Fleeting Wax', 'Dopeness Galore', 'Kompakt Pop', 'Modern Harmonic',
				'Canti Magnetici', 'NBAST', 'Nite Grooves', 'Knowone', 'Specials Worldwide', 'De La Groove', 'Thema',
				'Zodiac 44', 'Appian Sounds', 'Deepblak', 'PAL SL', 'Darla Records', 'Digitalo Enterprises', 'Blodörn',
				'CTI Records', 'Inkal', 'Coherent States', 'Jericho One', 'Telrae', 'Mu-Psych Music', 'Sukhumvit Records',
				'ever/never records', 'Key All', 'Agogo Records', 'Laton', 'Banned Production',
				'Whispering Signals', '96 And Forever Records', 'Cabrera', 'Tropical Jam', 'Off Me Nut Records',
				'Taverna Tracks', 'Worldwide Zen', 'Epm Music', 'Upon You Records', 'Spaziale Recordings',
				'Superfriends Records', 'R=A', 'We_r House', 'SVS Records', 'Psychemagik', 'Subosc LTD', 'Kniteforce White',
				'Hustler Trax', 'Dance Classics', 'Holy Roar Records', 'Unic', 'Soleilmoon Recordings',
				'Fent Plates', 'BEK Audio', 'Magic Carpet', 'Citi Records', 'Seil Records', 'Moon Harbour Recordings',
				'Musique Plastique', 'Hydra Head Records', 'Obscura', 'Pelagic Records',
				'Odd One Out', 'and/OAR', 'Kina Music', 'Iboga Records', 'HDTT High Definition Tape Transfers',
				'Hidden Hawaii Digital', 'Aesthetic Audio', 'Noise In My Head', 'Wax Trax! Records', 'Stereoscenic',
				'Mondo Groove', 'Fantasy Enhancing', 'Rejected', 'Collapsing Market', 'Garmo',
				'Deep Jazz Reality', 'Le Sablier', 'Sound Mirror', 'Modular Expansion Records', 'Tiff\'s Joints', 'Lossless',
				'Innate', 'Rhythm Nation', 'Quality Vibe Records', 'Network23', 'Goner Records', 'Slowdy Mowdy',
				'Running Out Of Steam', 'In My Dreams', 'Summer Isle', 'Last Known Trajectory',
				'In Silent Series', 'Spazio Records', 'Harbour City Sorrow', 'Cos_Mos', 'AC-EXP', 'Frigio Records',
				'Time Life Music', 'Chesky Records', 'Too Pure', 'Classic Production Osnabrück', 'CPO', 'Sdban', 'Omlott',
				'Lower Parts', 'Belpaese Edits', 'Rotten City Records', 'Cosmic Tones Records', 'More Than Human',
				'Air Miles', 'The Untold Stories', 'Intangible Records & Soundworks', 'Assemble Music', 'Sequencias',
				'No Bad Edits', 'Vernacular Records', 'Propersound', 'No Waves', 'Chapter 24 Records', 'Wierd Records',
				'Abstract Forms', 'Eremite Records', 'Affordable Inner Space', 'Moda Black', 'Pica Disk', 'Geophone',
				'Duality Trax', 'SUB tl', 'Hyades Arts', 'Etruria Beat', 'Blizzard Entertainment', 'Holy Mountain',
				'Tonika Recordings', 'HoZac Records', 'Sounds Of The City', 'Adult Contemporary', 'Orson Records',
				'SlapFunk Records', 'Duophonic', 'Phantasma Disques', 'Doomtree', 'Recycled Plastics', 'Verve Records',
				'Morning Trip Records', 'Warm Fiction', 'Still Music', 'Numero Group 800 Line', 'Rose Records',
				'Flash Forward', 'Zero Grow', 'Aura Expansion', 'I\'m Starting To Feel OK - Series', 'BEAM', 'Hibari Music',
				'Dred', 'Hasana Editions', 'A Sacred Geometry', 'Hyperboloid', 'AMOK Tapes', 'Mawimbi', 'De Afrekening',
				'Iron Bonehead Productions', '100% Pure', 'Metal Position Records', 'Mystery Sea', 'Elastic Dreams',
				'Irritable Hedgehog', 'Pusic Records', 'Injazero Records', 'Fragment Factory', 'Asking For Trouble',
				'No Problema Tapes', 'What About Never', 'World Unknown', 'Tribe Records', 'Disko B', 'Colorize',
				'Nachtstrom Schallplatten', 'Outis Music', 'Apple Pips', 'Métron Records', 'Wounded Bird Records',
				'Metamorphic Recordings', 'Smashing Tape Records', 'Psi49Net', 'What Now Becomes', 'Normandy Records',
				'Ill Behaviour', 'Swoon', 'Island Records', 'Numero Group 200 Line', 'lofi.hiphop',
				'Technical Equipment Supply', 'Brainwaves', 'Emerald', 'Alliance Upholstery', 'Zodiak Commune',
				'Matière Mémoire', 'Nostro Hood System', 'Prodigal Son', 'MOOD', 'R-Time Records',
				'Delirio', 'World Music', 'Certificate 18', 'Ice City Records', 'Telomere Plastic', 'Akashic Records',
				'Corbett vs. Dempsey', 'Bizarro Records', '1XA', 'Scion Versions', 'Infiltrate', 'MusicandPower',
				'Nashazphone', 'Animalia', 'SST Records', 'Memoria Recordings', 'OFF Recordings', 'Other World Music',
				'Aficionado Recordings', 'Hommage', 'Zoom Lens', 'Soulsity', 'Wakaan', 'Polish Jazz Series',
				'Senufo Editions', 'Understand', 'Secret Crunch', 'AHAAD', 'Délicieuse Records', 'Otaku Records',
				'Castles in Space', 'Vice Versa Records', 'End of the Alphabet', 'More Rice', 'Toi Toi Musik',
				'Dust Editions', 'Auf Abwegen', 'Dorfjungs', 'CAM', 'Skryptöm Records', 'Macro',
				'Häpna', 'Constant Black', 'Temporary State', 'Play It Say It', 'Cramps Records', 'Bedroom Suck Records',
				'Skint', 'Omega Supreme Records', 'Zement', 'Fracture Label', 'Musar Recordings', 'Inner Ocean Records',
				'Kompakt Exklusiv', 'Aim', 'Ballyhoo', 'Sum Over Histories', 'Nonlocal Research', 'Frantic Flowers',
				'Caoutchou Recordings', 'KAT', 'Metempsychosis Records', 'IL SILENZIO DEL RUMORE', 'Hundred Flowers', 'LYKA',
				'The Roundtable', 'Outlaw', 'trueColors', 'Tape Archive', 'Roadrunner Records', 'Night People',
				'Scopex & Solexis Audio',
			].map(normalizeName).join('|') + ')\\b)/i',
			'color: #8bb400; font-weight: normal;'
		], [ // RED top4000 subscribed labels
			'/^(?:(?:' + [
				'サウンド・フォレスト・シリーズ', 'H.O.M.', 'Eureka!', 'Enklav.', 'Наука и Искусство', 'Get The Balance Right!',
				'Touché', 'LL.M.', 'Hau Ruck!', '東京為替', 'Primary [colours]', 'Ecstatic Peace!', 'diametric.', 'Arf! Arf!',
				'必聴名盤 シリーズ', 'D.O.C.',
			].map(normalizeName).join('|') + ')|(?:' + [
				'Easy Street Records', 'Inermu / Inermu Wax', 'Ugly Music', 'Carpet & Snares Records', 'Café Kaput',
				'Molekül', 'Sound Forest', 'Asyncro', 'Baile Musik', 'House Jam Records',
				'Arc Light Editions', 'Home Taping Is Killing Music', 'Zig-Zag Territoires', 'Main Street Records',
				'Objektivity', 'Fag Tapes', 'Silent Season 10 Year Collection 2007-2017', 'LSO Live',
				'Alien Transistor', 'Laid', 'Neighbourhood', 'Reckno', 'Schimpfluch', 'Fonal records', 'Samosa Records',
				'SLEEPERS', 'Out Electronic Recordings', 'Out-Er', 'White', 'Equalized', 'Beachy Head', 'Street Beats',
				'Kess Kill', 'OODiscs', 'Neovinyl Recordings', 'Instinct Ambient', 'Nubiphone', 'Business Casual',
				'Merck', 'Lion Charge Records', 'V Recordings', 'Chained Library', 'Global Warming Records',
				'Pinchy & Friends', 'King Street Sounds', 'Ancient Future Now', 'Garage Hermétique', 'Enhanced Recordings',
				'Transgressive Records', 'Wichita', 'New Religion', 'Patron Records', 'FJAAK', 'Mort Aux Vaches',
				'Altered States Tapes', 'Towhead Recordings', 'Missile Records', 'Jazz Cabbage', 'Holodeck', 'Colorful World',
				'The Preservation Project', 'Subsist', 'Erratum', 'Science and Art', '17853 Records',
				'Heavenly', 'Rubisco', 'A State Of Trance', 'Lonely Planets Rec', 'Standalone Records',
				'La dame Noir Records', 'Team Sesh', 'Allnite Music', 'Wonder Stories', 'BP Mind Series', 'Erato',
				'No Hats No Hoods', 'Thalamos', 'Gang Of Ducks', 'Lost 80s Record Company', 'Hidden Vibes', 'Trapez',
				'Kneaded Pains', 'International Major Label', 'Jazz Is Dead', 'Pingipung', 'Neon Finger', 'Refreshers',
				'Division', 'Positiva', 'Mikroton Recordings', 'No Symbols', 'Mecanica', 'Run For Cover Records',
				'Natural Expressions', 'Helicopter', 'Mad About Records', 'Enterplanetary Koncepts', 'AvantRoots',
				'Future City Records', 'Zang Tumb Tuum', 'ZTT', 'Mirae Arts', 'Childsplay', 'Editainment', 'All Inn Records',
				'Figure Jams', 'Blackout Music NL', 'YNFND', 'TALISMANN', 'Staycore', 'Blu Mar Ten Music', 'Interpret',
				'Tear Apart Tapes', 'Hit+Run', 'Slack Trax', 'Alexandar', 'Cleopatra', 'Booma Collective',
				'Bloody Fist Records', 'Childhood', 'Relative', 'Salon', 'Drowned Records', 'Decca Records', 'The Other Side',
				'://about blank', 'ACR', 'Soft Computing', 'En/Of', 'Honey Butter Records', 'Electronique.it',
				'Cartulis Music', 'Absurd Recordings', 'Naxos Records', 'Steaward', '6dimensions',
				'Water', 'Texas Recordings Underground', 'Phobiq Recordings', 'Dirter Promotions', 'Home Invasion', 'Auroom',
				'Vakum Records', 'DPR', 'Dat Pressure Records', 'ESHU Records', 'Naked Music Recordings', 'Glossy Mistakes',
				'Circuit T Promo', 'To Rack & Ruin', 'Metal Blade Records', 'Tzadik: Composer Series', 'Poly Dance',
				'Barefoot Beats', 'Beer', 'Lustpoderosa', 'Mama Told Ya', 'Crydamoure', 'Crown Ruler Records',
				'Daehan Electronics', 'Genetic Music', 'Noorden', 'Casino Edits', 'Proton Music: Visceral', 'Fu.me', 'Ftarri',
				'Schwebung', 'Discos Atónicos', 'Big Sound Works', 'Simple Things Records', 'Noiztank', 'Systematic',
				'Academy LPs', 'Mad Habitat Recordings', 'Secondnature', 'Rakya Records', 'Globex Corp', 'Baroque Sunburst',
				'American Tapes', 'PRSPCT Recordings', 'Indole Records', 'Tale & Tone', 'Arbiter', 'Tome Records',
				'West End Communications', 'Fort Romeau', 'Dfsant', 'God Unknown Records', 'Talkin\' Loud', 'Cold Tonic',
				'Lenske', 'Serialism Records', 'Isophlux Records', 'I Dischi Del Barone', 'Codes', 'Grounded in Humanity',
				'Degustibus', 'In Records paris', 'Atlantic Rhythms', 'DAT Records', 'Young God Records', 'Graveface Records',
				'Ricercar', 'Fusion Diagnostics', 'No. 19', 'Tape Loop Orchestra', '88T / Syon', 'Repeat Concrete',
				'Donky Pitch', 'Shevchenko', 'Hi-Bias Records', 'Deep4Life', 'Cedesciu Wax', 'Second State', 'Lavibe',
				'Unbalance', 'Commmons', 'Stale', 'SPINNIN\' DEEP', 'Signature Records', 'Letters From Jerusalem',
				'Loonie Bin', 'Ultra', 'Intergalactic Research Institute For Sound', 'Bleeper', 'Matchless Recordings',
				'Taiga Records', 'SZE', 'Details Series', 'New Wilderness Audiographics', 'Cutters', 'The Press Group',
				'Djebali', 'Nomine Sound', 'Majestic Casual Records', 'Plastic City', 'Jazzy Sport', 'Vivus Records', 'Step',
				'Harmonie Park', 'Drumetrics', 'Polskie Nagrania Muza', 'Corwood Industries', 'Azzur',
				'Deep, Dark & Dangerous', 'Dark Jazz Records', 'Digital Hardcore Recordings', 'Burial Mix', 'Soul Notes',
				'Danza Nativa', 'Retreat', 'Courtesy Of Balance Recordings', 'Tevol', 'Cambria Instruments', 'Substrato',
				'Audiotree Live', 'EON Records', 'Pryda Presents', 'An\'archives', 'Suburban Avenue', 'Head Front Panel',
				'Cottam', 'Aperture', 'GFD', 'K2', 'Armada Deep', 'Club U Nite Records', 'Exart',
				'Pelican Fly', 'Toxic State', 'Enmossed', 'Kompakt Klassiks', 'Napalm Records', 'Patient Sounds Intl.',
				'Hardmatter', 'White House Records', 'Dum Records', 'Shadow Kingdom Records', 'Naïf', 'OVUNQVE',
				'Sleeperhold Publications', 'Stile Libero', 'Naural', 'Wist Rec.', 'Toolwaxx', 'Renegade Hardware',
				'Pi Recordings', 'Symbolic Interaction', 'Chrysopée Electronique - Bourges', 'Apersonal Music', 'Multinotes',
				'Extra Help', 'PURPLE POSSE', 'wewillalwaysbealovesong', 'Counterchange Recordings', 'Soblazn Music',
				'Tonzonen Records', 'Majia', 'PLUS100 Records', 'Citizens of Vice', 'Olo', 'Palette Recordings', 'Polymath',
				'Resonance Moscow', 'Kit Records', 'Silk Digital Records', 'Domina Trxxx', 'NG Trax', 'Takoma',
				'Blast First Petite', 'Unit Two', 'Silk Royal Records', 'Minimum Syndicat', 'Trax Couture', 'Confession',
				'Streamline', '30drop Records', 'Ultrastretch', 'Arupa Music', 'Die Stadt', 'Jaunt', 'Negentropy', 'Spclnch',
				'Woei', 'Supergenius Records', 'Ikuisuus', 'Tee Pee Records', 'PNL', 'Kiwi Rekords', 'DiG Records', 'Pure',
				'Arcana', 'Large Records', 'New Age Tapes', 'Jelly Bean Farm', 'Merkaba Music', 'Children of Tomorrow',
				'Rebound Lounge', 'Crime City Disco', 'Dee Jay Recordings', 'Par Avion', 'Prins Thomas Musikk',
				'Alfa Matrix', 'Earlydub Records', 'Oath', 'ICS Library', 'NoBusiness Records', 'Holic Trax', 'Hefty Records',
				'RogueArt', 'Slow Down Recordings', 'Oiwa', 'Boe Recordings', 'Headless Horseman', 'LumièresLaNuit',
				'Molten Moods', 'Le Chatroom', 'Hot N Ready', 'Beard Man', 'Too Rough 4 Radio', 'Metanoia', 'Sincopat',
				'Weekend Circuit', 'Crypt Records', 'Visions Inc', 'Bed of Nails', 'Hardly Art', 'Tasty Morsels',
				'Morris Audio', 'pure bread records ltd.', 'All Inn Limited', 'Captain Trip Records', 'Low Budget Family',
				'Empty Head Rich Heart', 'Hip-O Select', 'MPS Records', '012', 'Small Stone Records', 'Lakeshore Records',
				'Random Numbers', 'SUMAC', 'Modern Hypnosis', 'Breakbeat Kaos', 'Maurizio', 'Vulcan Venti',
				'Electronic Emergencies', 'Monika Enterprise', 'Yoga Records', 'SEALT', 'Magnet Records',
				'Queen Nanny Records', 'Barsuk Records', 'Body\'N Deep', 'Ge-stell', 'Ibadan Records', 'Alia Vox', 'U-Cover',
				'Relish Recordings', 'Underyourskin Records', 'ARTS WHITE', 'ICR', 'Sincerely Yours', 'Sub-Urban', 'Metaphon',
				'BVHaast', 'Stilove4music', 'Hooversound Recordings', 'Deeperama Mix Series', 'Sony Classical', 'RDV Music',
				'Nice \'N\' Ripe', 'DYAD', 'Music Interior', 'Padre Himalaya', 'angoisse', 'EYA Records', 'Möd3rn', 'F T D',
				'Intervision', 'Philpot', 'This Is It Forever', 'Pulsar Music', 'Pleasure Of Love', 'Body Fusion',
				'Not Two Records', 'Non-Temporal', 'Inner Sunset Recordings', 'Engineer Records', 'Discos Transgénero',
				'Lowmoneymusiclove', 'Krúnk', 'EarToGround Records', 'Obs.Cur', 'Hands Productions', 'Virus Recordings',
				'Construct Re-Form', 'Rough Grade', 'Treue Um Treue', 'Asphodel', 'Cakeman Records', 'Affected Music',
				'Upset! The Rhythm', 'Mörk White', 'Forest ill Records', 'Baltermore', 'Avantgarde Music', 'Lump Records',
				'INIT', 'Stax/Volt: The Original Era - 1959-1975', 'Connected Frontline', 'Distrikt Paris', 'Malonian',
				'Rummenigge', 'Foundation', 'Kanzleramt', 'Porridge Bullet / Pudru Kuul', 'Yupiteru Records',
				'Magdalena\'s Apathy', 'Astro Nautico', 'Secret Society', 'Body Theory', 'taâlem', 'Trumpett', 'Gen X',
				'Paradox Music', 'Long Hair Music', 'Nomark', 'Tapete Records', 'Antam Records', 'Echotourist',
				'Butterz', 'Urpa i musell', 'Areal Records', 'Sylphe', 'Little Beat Different', 'Underthesea', 'Batti Batti',
				'OM Records', 'Columbia Records', 'Phorma', 'Moira Audio Recordings', 'Armind', 'Never Say Die',
				'Nasoni Records', 'Hic Sunt Leones', 'Big Crown Records', 'Freestyle Records', 'No Fun Productions',
				'Brame & Hamo', 'Idealistmusic', 'Otographic Music', 'Amselcom', 'Legalize Lambada', 'Strawberry Rain',
				'RotterHague Records', 'Black Box', 'Ruere Records', 'Treisar', 'Culprit', 'HARDCORE TANO*C', 'Scissor Tail',
				'Dacapo', 'Echo Vortex', 'DJ Screw - Screwed Up Records Chapters', 'Brstl', 'LET Recordings', 'Rackmizar',
				'Dreamers', 'Databloem', 'One Records', 'Alienus', 'VDE-Gallo', 'Industrial Records', 'Anna Logue Records',
				'Zickzack', 'Numero Group: Asterisk', 'Tsunami Records', 'GOSU', 'Hardgroove', 'No U-Turn',
				'Healing Sound Propagandist', 'Monorail Trespassing', 'Yay Recordings', 'Hamam House', 'Intec', 'TT Label',
				'Treasure Series', 'Killekill House Trax', 'Black Venison', 'Vibes Ltd', 'True Playaz Music',
				'Eerik Inpuj Sound', 'Snuff Trax', 'Ganja Records', 'tekres', 'Loose Fit Records', 'Vivid',
				'Conceptual Records', 'Abstrakt Reflections', 'Atavistic', 'Typeless Records',
				'PROGRESSIVE FOrM', 'Emotive', 'PLMN', 'Chestplate', 'GOON TRAX', 'hello▼strange', 'Patchwork', 'Alma Negra',
				'Tokyo Exchange', 'Roux Records', 'Consouling Sounds', 'Halocline Trance', 'ExT Recordings',
				'False Industries', 'FLUF', 'Gottwax', 'DiN', 'Edits Des Amateurs', 'Hadal', 'Oxygen Music Works', 'OMW',
				'No Pain In Pop', 'Inner Surface Music', 'This Charming Man Records', 'Les Disques Du Soleil Et De L\'Acier',
				'Glass Talk', 'amenthia recordings', 'Golden Teacher', 'Poesie Musik', 'Petites Planètes', 'Dome Of Doom',
				'Parvati Records', 'Plop', 'Inspected', 'Schema Records', 'United States Of Mars',
				'Ophism', 'Light On Earth', 'Abduction', 'Bluespirit', 'Bondage Music', 'Contrast-Wax', 'Hashplant',
				'Werk__Ltd', '2L - Lindberg Lyd', 'Cold Blow Records', 'Banquise Recordings', 'Days Of Being Wild', 'Groove',
				'Super Disco Edits', 'LiveJam Records', 'RAAR', 'Symbolism', 'Cazeria Cazador', 'Memory No. 36 Recordings',
				'Apartment Records', 'Exosphere', 'RA-1', 'Alley Version', 'Paradise Is A Frequency',
				'Minuendo Recordings', 'Pentiments', 'Planet Sundae', 'Craft Recordings', 'The Senss', 'Rubber',
				'Neostrictly', 'Spinefarm Records', 'Several Minor Promises', 'Baumbaum Label', 'Dog Knights Productions',
				'Hobbies Galore', 'Tal der Verwirrung', 'Lolistyle Gabbers', 'P.W. Elverum & Sun', 'Obonit', 'TRANCEMAN2000',
				'Great Circles', 'Tesla Tapes', 'WWOZ on CD', 'Akbal Music', 'Network Records',
				'DAC Records', 'D\'Autres Cordes', 'Sister Midnight', 'Taped Artifact', 'Cult Metal Classics',
				'Leave The Man In Peace With His Kit', 'Patterns Of London', '10 Inches Of Pleasure Records', 'Droogs',
				'Roche Madame', 'We Are The Brave', 'Swamp Tapes', 'deFocus', 'Faith Beat', 'Squeeze The Lemon',
				'Musik Gewinnt Freunde', 'menstrualrecordings', 'Warm Sounds', 'Balearic', 'Deep & Roll', 'Italic',
				'00A', 'Abuser', 'Utility Plastics', 'Arrival', 'Fizic', 'Flying Dutchman', 'Amsem', 'Hells Headbangers',
				'Silk Sofa Music', 'Ebullition Records', 'MANHIGH Recordings', 'Silk Textures', 'Tombed Visions',
				'Fragil Musique', 'Merc', 'Bananafish', 'SIERRRA', 'Green Fetish Records', 'Human Pitch', 'Pole Jam Vinyl',
				'Dekorder', 'Drum&BassArena', 'Basement Records', 'Silk Selections', 'Esuoh White', 'IPSO', 'Parlophone',
				'RCA Victor Red Seal', 'Notice Recordings', 'Tesuji', 'United Dairies', 'URSL', 'Tiny Engines',
				'Eighteenth Street Lounge Music', 'Teerpappe', 'Undergroundtekno',
				'New Dawn of Japanese Rock - 70s Collection - P-Vine', 'YINYANG', 'Lal Lal Lal', 'TLFT', 'Okraina Records',
				'Käften', 'Salty Nuts', 'Valve Recordings', '3TH Records', 'Jarring Effects', 'JFX', 'Version Galore',
				'Madjazz Recordings', 'Other Voices', 'NV Rec', '8bit', 'Developer Archive', 'Sounds Of The Universe',
				'Basic Replay', 'Alien8 Recordings', 'Game Sound Museum', 'Vekton Black', 'Peppermint Jam', 'Soda Gong',
				'Cosmicleaf Records', 'Trapez LTD', 'Proper Trax', 'Wulf', 'Elektro Music Department', 'Seekers',
				'Other Ideas', 'Jolly Discs', 'Parallax Records', 'Musik Krause', 'Planet Core Productions', 'SDB Discos',
				'Naivety', 'Carebears', 'Krill Music', 'Ethereal Sound', 'Inwave', 'Into The Deep Records', 'Cave Recordings',
				'Foom', 'Sulk Magic', 'Synewave', 'Styrax Leaves', 'Nostalgie De La Boue', 'Rhythm Cult', 'Bliq',
				'Manuscript Records Ukraine', 'Creation Records', 'Deeply Cultured', 'Sign Bit Zero',
				'WXPN', 'Mechanical Reproductions', 'Rowle', 'Sungate', 'Mirare', 'Alphabasic',
				'New Albion Records', 'Troubadour Record', 'Kode', 'czaszka', 'Bunny Tiger',
				'Deutsche Harmonia Mundi', 'DHM', 'Invada', 'Locust', 'Motown Records', 'Touch Sensitive', 'Timeless',
				'Taro Records', 'Liftin\' Spirit Records', 'Owsley Stanley Foundation / Bear\'s Sonic Journals',
				'Cleaning Tapes', 'My Bags', 'Get Down Edits', 'Alku', 'Norma Evangelium Diaboli', 'Cityfox',
				'Sketches Records', 'Corpus Hermeticum', 'Paesaggi Records', 'Ongehoord', 'Seksound', 'Smalltown Superjazzz',
				'Raam', 'PRAH Recordings', 'Organized Music from Thessaloniki', 'House Running', 'Obscure Records',
				'Gynoid Audio', 'Third Place', 'Projekt', 'Order & Devotion', 'Horizon Records', 'Primitive Languages',
				'Future Retro', 'Jungle Label', 'Dispatch Recordings', 'Veleno Viola', 'Durtro',
				'Digitalis Recordings', 'Rosso Corsa Records', 'Gonzo', 'GLBDOM', 'Caldo Verde Records',
				'CD Ethnic Sound Series', 'First Terrace Records', 'Mosaique', 'Cultivated Electronics LTD', 'Record Kicks',
				'Ground Fault Recordings', 'Fallen Empire Records', 'Touchin\' Bass', 'Inhale Exhale Records',
				'Steinklang Industries', 'Syncrophone Recordings', 'Ze Records', 'Black Wattle', 'Beau Wanze',
				'Cuttin\' Headz', 'Non-Standard', 'Night Defined Recordings', 'Boogie Cafe Records', 'Slub Music',
				'Stereo Productions', 'Galate', 'Kunsthall Produktionen', 'Sommor', 'Leftfield Records', 'Pavilion',
				'Organic Industries', 'Anthology series compiled by Bill Brewster', 'Lukins', 'Zeon Light',
				'Global Underground: Adapt', 'Dim Mak Records', 'Rather Interesting', 'Time-Lag Records', 'Wah Wah Records',
				'C-KNOW-EVIL', 'Feedelity', 'videogamemusic', 'Polydor', 'Vehicle', 'Untidy', 'Unperceived Records',
				'Symmetry Recordings', 'Pest Productions', 'Enemies List Home Recordings', 'Auxilio De Cientos',
				'Fantastic Voyage', 'Tourette Records', 'Robot Records', 'Wolfskuil Ltd', 'Gagarin Records', 'Allchival',
				'Ismus', 'PushMaster Discs', 'RAUMKLANG MUSIC', 'Composers Recordings Inc.', 'CRI', 'Circus Records',
				'Tiercel', 'Teenbeat', 'Tigersushi', 'Atmospheric Existence Recordings', 'Nebulae Records', '12 Recs',
				'Brasilia Discos', 'Planet Phuture', 'Analog Versions', 'Winthorpe Electronics', 'Razormaid! Productions',
				'UntilMyHeartStops', 'Siege Records', 'Night Noise Music', 'N I G H T N O I S E', 'Brouqade Records',
				'Slagwerk', 'Donemus Composers\' Voice', 'Bromance Records', 'House of the Leg', 'One instrument',
				'Local Traffic', 'Hammock Music', 'Raw Russian', 'Tele Music', 'Full Of Nothing', 'Offworld Records',
				'Artoffact Records', 'Supraphon', 'Altar Records', 'Crucial Recordings', 'Keepers Of The Wild', 'WordSound',
				'W - I', 'Peur Bleue Records', 'Flux', 'Electric Minds', 'Clown and Sunset', 'Future Archive Recordings',
				'45Seven', 'OFFFM', 'Be Told Lies', 'agOgique', 'Ages Genesis', 'Phonogramme', 'Invite\'s Choice Records',
				'Verdant Recordings', 'Caim Records', 'Slow To Speak', 'Hooded Records', 'Rev-Ola', 'Minor Notes Recordings',
				'Bath House Etiquette', 'Fondamenta', 'El Cometa De Madrid', 'Tesco Organisation', 'RR', 'Raime\'s label',
				'Scopex', 'Eye Teeth', 'Round Bale Recordings', 'Planetaria Soundsystem', 'Alternative Tentacles',
				'TIP Records', 'Leveldva', 'Iberian Juke', 'Arkajo', 'Audiolith', 'Future Days Recordings', 'GAD Records',
				'Uncage', 'Cindys Tapes', 'Happy Camper Records', 'BNR Trax', 'Data Airlines', 'Kafta',
				'Sunday Breakfast', '70s Japanese Folk - URC', 'Listening Blue', 'Sono Unica', 'SOSO', 'Pafe Record',
				'Salin Records', 'Electro Music Coalition', 'S Y N T H', 'Pulp', 'Topic Records', 'Standard In-Fi',
				'Plant43 Recordings', 'Pearl', 'Siena', 'Blaufield Music', 'Bumpin\' City Records', 'Hyde Out Recordings',
				'Captcha Records', 'HBSP-2X', 'EMI Essential Listening Series', 'Karakul', 'Pan Records',
				'Len Leise', 'Signal Recordings', 'Take Away', 'Harlo', 'Kickin Records', 'Крым Мрык / Krym Mryk',
				'Tunnel Vision Records', 'Dessous Recordings', 'Kanzleramt - Albums & Compilations', 'Mole Listening Pearls',
				'Downhill Music', 'Hardline Sounds', 'Edizioni Musicali Delfino - Edizioni Musicali Volga', 'Wagon Repair',
				'Fataka', 'Neotantra', 'Samadhisound', 'Debacle Records', 'Drifting Over', 'Bigamo Musik', 'CR2 Records',
				'CP² Recordings', 'Tomlab', 'Mira', 'Native Response', 'Wunderblock Records', 'Sumo Smash Record',
				'In-Beat-Ween Music', 'Royal Sperm', 'Beste Freunde', 'URWAXX', 'Knitebreed Records', 'Poverty is violence',
				'We\'re Going Deep', 'Piano Music', 'Creative Source', 'Eine Welt', 'House Of EFUNK Records', 'Rad Cult',
				'Apus apus', 'Le Chant du Monde', 'Eating Records', 'Always Human Tapes', 'Grey Area',
				'Youth Attack', 'Futura Records', 'Aquarellist', 'Re.Face / Re.Face Limited', 'No Quarter',
				'Parallax Unknown', 'Third Wave Trax', 'Street Edits', 'Musikproduktion Dabringhaus und Grimm', 'mDG',
				'Commercial Suicide', 'Dirty Hands', 'Moment Of Truth', 'La Chèvre',
			].map(normalizeName).join('|') + ')\\b)/i',
			'color: darkgreen; font-weight: 300;'
		],
	]);
}

if (!markets || !Array.isArray(markets)) setDefaultMarkets();
if (!categories || !Array.isArray(categories)) setDefaultCategories();
if (!_QobuzGenresRating || typeof _QobuzGenresRating != 'object') setDefaultDiscounts();
_QobuzGenresRating.isIgnored = function(genre) { return this[Infinity] && this[Infinity].includes(genre) };
_QobuzGenresRating.computeQuality = function(genre) {
	let quality = 1;
	for (let discount in this) {
		if (Number(discount) > 0 && Array.isArray(this[discount]) && this[discount].includes(genre))
			quality -= Number(discount);
	};
	return quality;
};
if (!labelStyles || !Array.isArray(labelStyles)) setDefaultLabelStyles();
labelStyles = labelStyles.map(function(ls, ndx) {
	const rxParser = /^\/(.+)\/(\w*)$/;
	function regexpize(expr) {
		let m = rxParser.exec(expr);
		return m && new RegExp(m[1], Array.from(m[2].toLowerCase()).filter(flag => 'igmuy'.includes(flag)).join(''));
	}
	if (Array.isArray(ls[0]))
		ls[0] = ls[0].map(c => rxParser.test(c) ? regexpize(c) : c).filter(c => typeof c == 'string' || c instanceof RegExp);
	else if (rxParser.test(ls[0])) ls[0] = regexpize(ls[0]);
	return ls;
}).filter(ls => typeof ls[0] == 'string' || ls[0] instanceof RegExp || Array.isArray(ls[0]) && ls[0].length > 0);

document.head.appendChild(document.createElement('style')).innerHTML = `
div.totals { margin-top: 5pt; color: black; }
div.totals > span:not(:first-of-type) { margin-left: 1em; }

button.button {
	position: relative;
	padding: 1px 10px;
	border: none;
	color: white; font: bold larger "Segoe UI", sans-serif;
	opacity: 0.3;
}

button.sidebutton {
	width: 100%; height: 27pt;
	border: solid 2px #c9d4e0;
	user-select: none;
}

button.button.lightslategrey { background-color: lightslategrey; }
button.button.darkcyan { background-color: darkcyan; }
button.button.steelblue { background-color: steelblue; }

button.button.enabled:hover, div.dynfilters.enabled > div:hover, div.product > label.reviewed:hover {
	background-color: darkorange; color: white; transition: 0.25s;
}
.enabled { opacity: 1 !important; }

span.button-wrapper {
	float: right;
	margin: 0px 2px;
}

button.button-progress {
	cursor: progress;
	text-align: left;
	padding-left: 10px; padding-right: 10px;
	background-repeat: no-repeat;
	background-position-x: right 5px;
	background-position-y: center;
}

a.blinking { animation: blinking 1s infinite; }
@keyframes blinking {
	20% { opacity: 1.0; }
	30% { opacity: 0.5; }
	70% { opacity: 0.5; }
	80% { opacity: 1.0; }
}

div.dynfilters {
	background-color: olive;
	padding: 8px 0;
	border-radius: 8px;
	color: white; text-align: left;
	opacity: 0.3;
}
div.dynfilter { padding: 1px 12pt; }
div.dynfilter label {
	margin: 0; padding: 6px 0;
	font: 600 10pt/0 "Segoe UI", sans-serif;
	cursor: pointer; user-select: none;
}
div.dynfilter input[type="checkbox"] { float: none; margin: 0 8px 2px 0; }
div.scanners { margin-top: 10px; }

label.checkbox-expander {
	position: absolute;
	top: 0; right: 0;
	width: auto; margin: 0;
	line-height: 0;
	cursor: pointer;
}
div.product > label.reviewed { padding: 10px; }

a.google-link {
	height: auto;
	padding: 3px 26px 3px;
	background: #DDDA;
	border-radius: 5px;
}

a.deezer-link {
	width: 90px; height: 18px; padding: 3px 0;
}

div.album-cover img.pa-sticker { position: absolute; bottom: -5px; right: -5px; }
div.album-cover img.hires-sticker { position: absolute; top: 5px; left: 5px; }

div.detail > p.data.overflow > a:hover { text-decoration: none; }

span#items-counter {
	margin-left: 10px; float: right;
	min-width: 2.8em; text-align: right;
	font: medium Verdana, "Segoe UI", sans-serif;;
}
`;

function sortLinkParams(a) {
	console.assert(a instanceof HTMLAnchorElement, 'a instanceof HTMLAnchorElement');
	if (!(a instanceof HTMLAnchorElement)) return;
	let url = new URL(a);
	url.searchParams.sort();
	a.search = url.searchParams;
}

function incSi() { if (si instanceof HTMLElement) ++si.textContent }
function decSi() { if (si instanceof HTMLElement) --si.textContent }

function updateSi() {
	if (si instanceof HTMLElement) si.textContent =
		Array.from(document.body.querySelectorAll('div.search-results > div.product'))
			.filter(div => div.style.display != 'none').length;
	// document.body.querySelectorAll('div.search-results > div.product:not([style*="display: none" i])').length;
}

function isFiltered(article, onlyAvailOnDeezer = false, onlyReadOnDeezer = false) {
	console.assert(article instanceof HTMLElement, 'elem instanceof HTMLElement');
	if (!(article instanceof HTMLElement)) return;
	if (isSearch && !filterSearches) return false;
	let elem;
	return article.rating < (dynFilters.noRatingDiscount ? 1 : 0.1)
		|| dynFilters.onlyLp && !(article.duration >= lpThreshold) || dynFilters.onlyHires && !article.hires
		|| dynFilters.priceAbove && !(article.price >= priceThreshold) || dynFilters.noGhostItems && article.notFound
		|| dynFilters.onlyUnseen && article.isProcessed
		|| dynFilters.onlyRepBrand && ((elem = article.querySelector('span.brand')) == null
			|| !Array.from(elem.classList).some(cls => cls.startsWith('label-group-')))
		|| onlyAvailOnDeezer && !(article.dzStatus > 0) || onlyReadOnDeezer && !(article.dzStatus >= 4)
		|| dynFilters.onlyOneTracks && !(article.totalTracks == 1 && article.duration <= singleThreshold);
}

function applyDisplayFilters() {
	let counter = 0;
	for (let div of document.body.querySelectorAll('div.search-results > div.product')) {
		if (isFiltered(div, onlyAvailOnDeezer, onlyReadOnDeezer)) {
			if (div.style.display != 'none') div.style.display = 'none';
		} else {
			++counter;
			if (div.style.display == 'none') {
				for (let img of div.getElementsByTagName('IMG'))
					if (img.dataset.src && img.src.toLowerCase().endsWith('/blank.png')) img.src = img.dataset.src;
				div.style.display = null;
			}
		}
	}
	if (si instanceof HTMLElement) si.textContent = counter;
}

let safeHost = queryIpregistry(ipregistryToken).catch(function(reason) {
	if (reason == 'Unsafe host') return Promise.reject(reason);
	if (typeof reason == 'object') console.warn('Ipregistry error:', reason);
	return queryIpify(ipifyToken, ['Karneval Media']).catch(function(reason) {
		if (typeof reason == 'object') console.warn('Ipify error:', reason);
		return Promise.reject('No more services')
	});
});

function enableButtons(enable = true, button = undefined) {
	const isEnabled = elem => button instanceof HTMLElement ? enable || elem == button
			: enable && elem.id != 'all-checked';
	for (var elem of document.body.querySelectorAll('button.button, input.dynfilter[type="checkbox"], label.dynfilter > input[type="checkbox"]')) {
		elem.disabled = !isEnabled(elem);
		elem.style.pointerEvents = isEnabled(elem) ? 'auto' : 'none';
	}
	for (elem of document.body.querySelectorAll('button.button, div.dynfilters'))
		elem.classList[isEnabled(elem) ? 'add' : 'remove']('enabled');
// 		elemclassList[enable && (elem.id != 'all-checked' || button instanceof HTMLElement)
// 			|| button instanceof HTMLElement && elem == button ? 'add' : 'remove']('enabled');
}

let ref, elem1, elem2;
if (isBrowse && (ref = document.querySelector('span.search-result-info')) != null) {
	var si = document.createElement('span');
	si.id = 'items-counter';
	ref.parentNode.insertBefore(si, ref);
}

Promise.all(scanPage()).then(function(articles) {
	enableButtons(true);
	if (redWorkers.length > 0) Promise.all(redWorkers).then(redScanDone);
	saveRlsCaches();
});

if (pageControls != null) {
	if (page > 1) pageControls.insertBefore(createPageShortcut(1), nextPage != null ? nextPage.parentNode : null);
	for (let p = 5; p <= 50; p += 5)
		pageControls.insertBefore(createPageShortcut(p), nextPage != null ? nextPage.parentNode : null);
}

if (typeof GM_registerMenuCommand == 'function') GM_registerMenuCommand('Reset to defaults', function() {
	if (!confirm('Reset all parameters to default?')) return;
	setDefaultMarkets();
	setDefaultCategories();
	setDefaultDiscounts();
	setDefaultLabelStyles();
	document.location.reload();
}, 'R');

if (isSearch && !filterSearches) return;

if (isBrowse && (ref = document.querySelector('span.search-result-info')) != null) {
	if (!document.location.pathname.includes('/label/')) {
		// Rescan Deezer
		elem1 = document.createElement('span');
		elem1.className = 'button-wrapper';
		elem2 = document.createElement('button');
		elem2.textContent = 'Rescan Deezer';
		elem2.className = 'button darkcyan';
		elem2.style = 'min-width: 12em; height: 24px; display: none;';
		elem2.disabled = true;
		elem2.id = 'rescan-deezer';
		elem2.onclick = deezerRescan;
		elem1.append(elem2);
		ref.parentNode.insertBefore(elem1, ref);

		// Mark all
		elem1 = document.createElement('span');
		elem1.className = 'button-wrapper';
		elem2 = document.createElement('button');
		elem2.textContent = 'All checked';
		elem2.className = 'button darkcyan';
		elem2.style = 'min-width: 12em; height: 24px;';
		elem2.disabled = true;
		elem2.id = 'all-checked';
		elem2.onclick = markAllProcessed;
		elem1.append(elem2);
		ref.parentNode.insertBefore(elem1, ref);
	}

	if (nextPage) {
		sortLinkParams(nextPage);
		elem1 = nextPage.parentNode.cloneNode(true);
		elem1.style = `
float: right;
margin-right: 5px;
background-color: white;;
border: thin solid lightslategrey;
text-align: center;
`;
		elem1.children[0].style = `
float: left;
padding: 1px 12px;
line-height: 18px;
text-decoration: none;
background-color: #ffffff;
border: 1px solid #dddddd;
border-left-width: 0;
`;
		elem1.id = 'nextpage-clone';
		//sortLinkParams(elem1.children[0]);
		ref.parentNode.insertBefore(elem1, ref);
	}

	elem1 = document.createElement('span');
	elem1.textContent = page;
	elem1.style = `
float: right;
margin-left: 10px;
min-width: 30px; height: 23px;
background-color: papayawhip;
text-align: center;
font: bold larger "Segoe UI";
`;
	elem1.id = 'page-indicator';
	ref.parentNode.insertBefore(elem1, ref);

	ref.append(' (');
	var pi = document.createElement('b');
	pi.id = 'processed-counter';
	pi.textContent = processedItems.size;
	ref.append(pi);
	ref.append(' processed)');
}

if (isBrowse && !document.location.pathname.includes('/label/') && (ref = document.getElementById('right-column')) != null) {
	// Near upcoming scanners
	let container = document.createElement('div');
	container.className = 'scanners';
	container.style.marginBottom = '20px';
	// Out today
	elem2 = document.createElement('button');
	elem2.className = 'button sidebutton darkcyan';
	elem2.id = 'filter-today-ag-am';
	elem2.textContent = 'Today (AG/AM)';
	elem2.title = 'Across all genres / all markets (+Ctrl: AM only / +Shift: AG only / +Ctrl+Shift: only in this category; +Alt: alternate lookup method, may bring results if default method fails)';
	elem2.onclick = lookup;
	elem2.AM = true;
	elem2.AG = true;
	elem2.useModifiers = true;
	elem2.NCS = true;
	elem2.timeFilter = [new Date().getDateValue()];
	elem2.disabled = true;
	container.append(elem2);
	// Out tomorrow
	let timeFilter = new Date(), wDay = timeFilter.getDay();
	timeFilter.setDate(timeFilter.getDate() + 1);
	elem2 = document.createElement('button');
	elem2.className = 'button sidebutton darkcyan';
	elem2.id = 'filter-tomorrow-ag-am';
	elem2.textContent = 'Tomorrow (AG/AM)';
	elem2.title = 'Across all genres / all markets (+Ctrl: AM only / +Shift: AG only / +Ctrl+Shift: only in this category)';
	elem2.onclick = lookup;
	elem2.AM = true;
	elem2.AG = true;
	elem2.useModifiers = true;
	elem2.disabled = true;
	elem2.timeFilter = [timeFilter.getDateValue()];
	container.append(elem2);
	// Out this week except friday
	timeFilter = [new Date(), new Date()];
	timeFilter[1].setDate(timeFilter[1].getDate() + (11 - timeFilter[1].getDay()) % 7);
	timeFilter = timeFilter.map(d => d.getDateValue());
	elem2 = document.createElement('button');
	elem2.className = 'button sidebutton darkcyan';
	if (wDay >= 4 && wDay <= 5) elem2.style.display = 'none';
	elem2.id = 'filter-thisweek-ag-am';
	elem2.textContent = 'Up to Friday (AG/AM)';
	elem2.title = 'Across all genres / all markets (+Ctrl: AM only / +Shift: AG only / +Ctrl+Shift: only in this category)';
	elem2.onclick = lookup;
	elem2.AM = true;
	elem2.AG = true;
	elem2.useModifiers = true;
	elem2.timeFilter = timeFilter;
	elem2.disabled = true;
	container.append(elem2);
	// Out this Friday
	timeFilter = new Date();
	timeFilter.setDate(timeFilter.getDate() + (12 - timeFilter.getDay()) % 7);
	elem2 = document.createElement('button');
	elem2.className = 'button sidebutton steelblue';
	if (wDay >= 4 && wDay <= 5) elem2.style.display = 'none';
	elem2.id = 'filter-friday-ag-am';
	elem2.textContent = 'Upcoming Friday (AG/AM)';
	elem2.title = 'Across all genres / all markets (+Ctrl: AM only / +Shift: AG only / +Ctrl+Shift: only in this category)';
	elem2.onclick = lookup;
	elem2.AM = true;
	elem2.AG = true;
	elem2.useModifiers = true;
	elem2.disabled = true;
	elem2.timeFilter = [timeFilter.getDateValue()];
	container.append(elem2);
	// Commng soon
	elem2 = document.createElement('button');
	elem2.className = 'button sidebutton darkcyan';
	elem2.style.display = 'none';
	elem2.id = 'filter-comingsoon-ag-am';
	elem2.textContent = 'Coming soon (AG/AM)';
	elem2.title = 'Across all genres / all markets (+Ctrl: AM only / +Shift: AG only / +Ctrl+Shift: only in this category)';
	elem2.onclick = lookup;
	elem2.AM = true;
	elem2.AG = true;
	elem2.useModifiers = true;
	elem2.disabled = true;
	elem2.timeFilter = [new Date().getDateValue(), Infinity];
	container.append(elem2);

	ref.insertBefore(container, ref.children[0].nextElementSibling);

	// Upcoming scanners
	container = document.createElement('div');
	container.className = 'scanners';
	// New in category
	elem2 = document.createElement('button');
	elem2.className = 'button sidebutton lightslategrey';
	elem2.id = 'filter-new';
	elem2.textContent = 'Lookup newly added';
	elem2.title = 'Only in this category (+Ctrl: bypass cache)';
	elem2.onclick = lookup;
	elem2.timeFilter = true;
	elem2.disabled = true;
	container.append(elem2);
	// New AG
	elem2 = document.createElement('button');
	elem2.className = 'button sidebutton lightslategrey';
	elem2.id = 'filter-new-ag';
	elem2.textContent = 'Lookup AG newly added';
	elem2.title = 'Across all genres (+Ctrl: bypass cache)';
	elem2.onclick = lookup;
	elem2.AG = true;
	elem2.timeFilter = true;
	elem2.disabled = true;
	container.append(elem2);
	// New AM
	elem2 = document.createElement('button');
	elem2.className = 'button sidebutton lightslategrey';
	elem2.id = 'filter-new-am';
	elem2.textContent = 'Lookup AM newly added';
	elem2.title = 'Across all markets (+Ctrl: bypass cache)';
	elem2.onclick = lookup;
	elem2.AM = true;
	elem2.timeFilter = true;
	elem2.disabled = true;
	container.append(elem2);
	// New AG/AM
	elem2 = document.createElement('button');
	elem2.className = 'button sidebutton lightslategrey';
	elem2.id = 'filter-new-ag-am';
	elem2.textContent = 'Lookup AG/AM newly added';
	elem2.title = 'Acros all genres / all markets - many requests, time expensive (+Ctrl: bypass cache)';
	elem2.onclick = lookup;
	elem2.AM = true;
	elem2.AG = true;
	elem2.timeFilter = true;
	elem2.disabled = true;
	container.append(elem2);

	ref.insertBefore(container, ref.children[0].nextElementSibling);
}

if ((ref = document.getElementById(isBrowse ? 'right-column' : isSearch ? 'left-column' : undefined)) != null) {
	let container = document.createElement('div');
	container.className = 'dynfilters';

	// No ghost items
	elem1 = document.createElement('label');
	elem1.className = 'dynfilter';
	elem1.title = 'Restrict view to only releases with existing details';
	elem2 = document.createElement('input');
	elem2.type = 'checkbox';
	elem2.className = 'dynfilter';
	elem2.id = 'dynfilter-no-ghost-items';
	elem2.checked = dynFilters.noGhostItems;
	elem2.disabled = true;
	elem2.onchange = function(evt) {
		dynFilters.noGhostItems = evt.currentTarget.checked;
		GM_setValue(filtersKeyname, dynFilters);
		applyDisplayFilters();
	};
	elem1.append(elem2);
	elem1.append('Only existing releases');
	elem2 = document.createElement('div');
	elem2.className = 'dynfilter checkbox';
	elem2.append(elem1);
	container.append(elem2);

	// Rating filter
	elem1 = document.createElement('label');
	elem1.className = 'dynfilter';
	elem1.title = `Restrict view to only releases without any rating discounts
(rating is computed out of genre and duration or standard quality price)

discounts by genre can be customized via local storage entry genres_rating`;
	elem2 = document.createElement('input');
	elem2.type = 'checkbox';
	elem2.className = 'dynfilter';
	elem2.id = 'dynfilter-full-rating';
	elem2.checked = dynFilters.noRatingDiscount;
	elem2.disabled = true;
	elem2.onchange = function(evt) {
		dynFilters.noRatingDiscount = evt.currentTarget.checked;
		GM_setValue(filtersKeyname, dynFilters);
		applyDisplayFilters();
	};
	elem1.append(elem2);
	elem1.append('Only releases without rating discount');
	elem2 = document.createElement('div');
	elem2.className = 'dynfilter checkbox';
	elem2.append(elem1);
	container.append(elem2);

	// Reputable labels filter
	elem1 = document.createElement('label');
	elem1.className = 'dynfilter';
	elem1.title = `Restrict view to only releases of labels with reputable (known) name

List of known labels can be customized via local storage entry label_styles`;
	elem2 = document.createElement('input');
	elem2.type = 'checkbox';
	elem2.className = 'dynfilter';
	elem2.id = 'dynfilter-rep-brands';
	elem2.checked = dynFilters.onlyRepBrand;
	elem2.disabled = true;
	elem2.onchange = function(evt) {
		dynFilters.onlyRepBrand = evt.currentTarget.checked;
		GM_setValue(filtersKeyname, dynFilters);
		applyDisplayFilters();
	};
	elem1.append(elem2);
	elem1.append('Only releases of reputable labels');
	elem2 = document.createElement('div');
	elem2.className = 'dynfilter checkbox';
	elem2.append(elem1);
	container.append(elem2);

	// Full-length
	elem1 = document.createElement('label');
	elem1.className = 'dynfilter';
	elem1.title = 'Restrict view to only long play (30 minutes and more) releases, ie. exclude singles and EPs';
	elem2 = document.createElement('input');
	elem2.type = 'checkbox';
	elem2.className = 'dynfilter';
	elem2.id = 'dynfilter-longplay';
	elem2.checked = dynFilters.onlyLp;
	elem2.disabled = true;
	elem2.onchange = function(evt) {
		dynFilters.onlyLp = evt.currentTarget.checked;
		GM_setValue(filtersKeyname, dynFilters);
		applyDisplayFilters();
	};
	elem1.append(elem2);
	elem1.append('Only full albums (est.)');
	elem2 = document.createElement('div');
	elem2.className = 'dynfilter checkbox';
	elem2.append(elem1);
	container.append(elem2);

	// Minimum price filter
	elem1 = document.createElement('label');
	elem1.className = 'dynfilter';
	elem1.title = 'Restrict view to only releases above defined price (can be customized via storage entry price_threshold)';
	elem2 = document.createElement('input');
	elem2.type = 'checkbox';
	elem2.className = 'dynfilter';
	elem2.id = 'dynfilter-price-above';
	elem2.checked = dynFilters.priceAbove;
	elem2.disabled = true;
	elem2.onchange = function(evt) {
		dynFilters.priceAbove = evt.currentTarget.checked;
		GM_setValue(filtersKeyname, dynFilters);
		applyDisplayFilters();
	};
	elem1.append(elem2);
	elem1.append('Only releases above ' + priceThreshold.toFixed(2) + ' €');
	elem2 = document.createElement('div');
	elem2.className = 'dynfilter checkbox';
	elem2.append(elem1);
	container.append(elem2);

	// HI-RES filter
	elem1 = document.createElement('label');
	elem1.className = 'dynfilter';
	elem2 = document.createElement('input');
	elem2.type = 'checkbox';
	elem2.className = 'dynfilter';
	elem2.id = 'dynfilter-hires';
	elem2.checked = dynFilters.onlyHires;
	elem2.disabled = true;
	elem2.onchange = function(evt) {
		dynFilters.onlyHires = evt.currentTarget.checked;
		GM_setValue(filtersKeyname, dynFilters);
		applyDisplayFilters();
	};
	elem1.append(elem2);
	elem1.append('Only releases with Hi-Res version');
	elem2 = document.createElement('div');
	elem2.className = 'dynfilter checkbox';
	elem2.append(elem1);
	container.append(elem2);

	// Only unread (not processed) items
	elem1 = document.createElement('label');
	elem1.className = 'dynfilter';
	elem1.title = 'Restrict view to only unchecked items';
	elem2 = document.createElement('input');
	elem2.type = 'checkbox';
	elem2.className = 'dynfilter';
	elem2.id = 'dynfilter-unseen';
	elem2.checked = dynFilters.onlyUnseen;
	elem2.disabled = true;
	elem2.onchange = function(evt) {
		dynFilters.onlyUnseen = evt.currentTarget.checked;
		GM_setValue(filtersKeyname, dynFilters);
		applyDisplayFilters();
	};
	elem1.append(elem2);
	elem1.append('Only unchecked releases');
	elem2 = document.createElement('div');
	elem2.className = 'dynfilter checkbox';
	elem2.append(elem1);
	container.append(elem2);

	// Only 1-track singles
	elem1 = document.createElement('label');
	elem1.className = 'dynfilter';
	elem2 = document.createElement('input');
	elem2.type = 'checkbox';
	elem2.className = 'dynfilter';
	elem2.id = 'dynfilter-onetracks';
	elem2.checked = dynFilters.onlyOneTracks;
	elem2.disabled = true;
	elem2.onchange = function(evt) {
		dynFilters.onlyOneTracks = evt.currentTarget.checked;
		GM_setValue(filtersKeyname, dynFilters);
		applyDisplayFilters();
	};
	elem1.append(elem2);
	elem1.append('Only one-track singles');
	elem2 = document.createElement('div');
	elem2.className = 'dynfilter checkbox';
	elem2.append(elem1);
	container.append(elem2);

	// Only releases available on Deezer
	elem1 = document.createElement('label');
	elem1.className = 'dynfilter';
	elem1.title = 'Restrict view to only releases having it\'s counterpart on Deezer';
	elem2 = document.createElement('input');
	elem2.type = 'checkbox';
	elem2.className = 'dynfilter';
	elem2.id = 'dynfilter-only-on-deezer';
	elem2.checked = onlyAvailOnDeezer;
	elem2.disabled = true;
	elem2.onchange = function(evt) {
		onlyAvailOnDeezer = evt.currentTarget.checked;
		applyDisplayFilters();
	};
	elem1.append(elem2);
	elem1.append('Only releases available on Deezer');
	elem2 = document.createElement('div');
	elem2.className = 'dynfilter checkbox';
	elem2.style.display = 'none';
	elem2.append(elem1);
	container.append(elem2);

	// Only releases available for download on Deezer
	elem1 = document.createElement('label');
	elem1.className = 'dynfilter';
	elem1.title = 'Restrict view to only releases downloadable from Deezer';
	elem2 = document.createElement('input');
	elem2.type = 'checkbox';
	elem2.className = 'dynfilter';
	elem2.id = 'dynfilter-only-dl-deezer';
	elem2.checked = onlyReadOnDeezer;
	elem2.disabled = true;
	elem2.onchange = function(evt) {
		onlyReadOnDeezer = evt.currentTarget.checked;
		applyDisplayFilters();
	};
	elem1.append(elem2);
	elem1.append('Only releases downloadable from Deezer');
	elem2 = document.createElement('div');
	elem2.className = 'dynfilter checkbox';
	elem2.style.display = 'none';
	elem2.append(elem1);
	container.append(elem2);

	ref.insertBefore(container, ref.children[0].nextElementSibling);
}

return;

function createPageShortcut(pageNo) {
	let li = document.createElement('LI');
	if (pageNo != page) {
		let a = document.createElement('A');
		a.title = 'Page ' + pageNo;
		a.textContent = pageNo;
		a.href = genPageUrl(pageNo);
		li.append(a);
	} else {
		let span = document.createElement('SPAN');
		span.textContent = pageNo;
		span.style = 'color: orange; font-weight: bold;';
		li.append(span);
	}
	return li;
}

function computeQuality(article) {
	if (_QobuzGenresRating.isIgnored(article.genre)) return -Infinity;
	let quality = 1;
	if (vaParser.test(article.artist)) quality -= 0.20;
	for (let discount in _QobuzGenresRating) if (Number(discount) > 0 && Array.isArray(_QobuzGenresRating[discount])
			&& _QobuzGenresRating[discount].includes(article.genre)) quality -= Number(discount);
	if (/\b(?:Christmas|X-?Mas|^Mega\s+Tree$)\b/i.test(article.album)) quality -= 0.30;
	if (/[\u0100-\uffff]{5,}/.test(article.album.replace(/\s+/g, ''))
			|| /[\u0100-\uffff]{5,}/.test(article.artist.replace(/\s+/g, ''))) quality -= 0.50;
	if (!article.duration) {
		if (article.price < 5) quality -= (5 - article.price) / 10;
	} else if (article.duration < 15 * 60) quality -= 0.3;
	//console.debug('computeQuality:', article, quality);
	return quality;
}

function redScanDone() {
	let container = document.querySelector('div.search-results > div.pagination');
	if (container == null) {
		if ((container = document.getElementById('main-column')) == null) return;
		var div = document.createElement('div');
		div.id = 'red-status-outer';
		div.style = 'margin-top: 10px; width: 100%';
		container = container.appendChild(div);
	}
	div = document.createElement('div');
	div.id = 'red-status';
	div.style = 'float: right; margin-right: 10px;';
	div.innerHTML = '<img src="" style="width: 30px;">';
	container.append(div);
}

function visualizeGazelleStatus(article) {
	return (function() {
		if (notSiteArtistsCache.has(article.artist)) return Promise.resolve(null);
		if (siteArtistsCache.has(article.artist)) return Promise.resolve(siteArtistsCache.get(article.artist));
		if (article.releaseDate < today || vaParser.test(article.artist)
				|| processedItems.has(getIdFromUrl(article))) return Promise.resolve(undefined);
		return queryAjaxAPI('redacted.ch', 'artist', {
			artistname: article.artist,
			artistreleases: true,
		}).then(function(artist) {
			siteArtistsCache.set(article.artist, artist);
			return artist;
		}).catch(function(reason) {
			if (reason != 'no artist found') return Promise.reject(reason);
			notSiteArtistsCache.add(article.artist);
			return null;
		});
	})().then(function(artist) {
		if (artist === null) {
			article.style.backgroundColor = / & |\s+and\s+|, /.test(article.artist) ?
				'rgba(64, 0, 150, 0.10)' : 'rgba(64, 0, 150, 0.30)';
		} else if (typeof artist == 'object') {
			let isRequested = artist.requests.filter(request => {
				return request.title.toLowerCase() == article.album.toLowerCase();
			}).length > 0;
			let groups = {};
			artist.torrentgroup.forEach(torrentGroup => {
				if (!Array.isArray(groups[torrentGroup.releaseType])) groups[torrentGroup.releaseType] = [];
				groups[torrentGroup.releaseType].push(torrentGroup);
			});
		}
	}).catch(reason => { console.debug(article.artist, 'not colourized for the reason:', reason) });
	if (processedItems.has(getIdFromUrl(article.querySelector('div.album-title > a')))) return;
}

function queryDeezerStatus(article) {
	const a = article.querySelector('div.action > ul > li > a');
	if (a == null) return Promise.reject('Assertion failed (a == null)');
	if (article.style.display == 'none' && !isFiltered(article)) {
		article.style.display = null;
		incSi();
	}
	a.style.visibility = null;
	a.style.opacity = null;
	a.style.backgroundColor = null;
	a.classList.remove('blinking');
	a.removeAttribute('title');
	a.childNodes.forEach(child => { if (child.nodeName != 'IMG') a.removeChild(child) });
	return queryDeezerAPI('search/album', {
		q: a.dzSearchTerm,
		strict: 'on',
		order: 'RANKING',
	}).then(function(results) {
		function applyAlbumStatus(status) {
			if (typeof status != 'number') return;
			const lastStatus = article.dzStatus;
			article.dzStatus = status;
			if (isFiltered(article, onlyAvailOnDeezer, onlyReadOnDeezer)) {
				if (article.style.display != 'none') {
					article.style.display = 'none';
					decSi();
				}
			} else if (article.style.display == 'none') {
				article.style.display = null;
				incSi();
			}
			switch (status) {
				case -1: // dead zero
					a.style.backgroundColor = 'red';
					a.title = 'No matches on Deezer at all';
					break;
				case 0: // zero matches after analysing results
					a.style.backgroundColor = '#ff000080';
					a.title = 'Not matches on Deezer close this release';
					break;
				case 1: // 2 and more matches
					//a.style.backgroundColor = null;
					a.title = 'More matches on Deezer (ambiguous)';
					break;
				case 2: // weak match without release detail
					a.style.backgroundColor = '#0808';
					a.title = 'More matches on Deezer (release details not get)';
					break;
				case 3: // verified match not ready for download
					a.style.backgroundColor = '#c0c000';
					a.title = 'Verified match on Deezer (not ready for download)';
					break;
				case 4: // verified match (complete for dl)
					a.style.backgroundColor = 'green';
					a.title = 'Verified match on Deezer ready for download';
					break;
			}
			if (!isNaN(lastStatus) && status != lastStatus) {
				a.title = 'Status changed from ' + lastStatus + ' to ' + status;
				console.log('Deezer status changed for', article.artist, '-', article.album, lastStatus, '=>', status, results);
				a.classList.add('blinking');
				if (status >= 2) {
					let dzSignal = document.getElementById('deezer-change-counter');
					if (dzSignal != null) ++dzSignal.innerText; else {
						dzSignal = document.createElement('div');
						dzSignal.innerHTML = 'There are Deezer status<br>changes in availability (<span id="deezer-change-counter">1</span>)';
						dzSignal.id = 'deezer-change-indicator';
						dzSignal.style = 'color: whitesmoke; border: whitesmoke solid 5px; background-color: darkslategray; ' +
							'padding: 10px; position: fixed; bottom: 20px; right: 50px; font: bolder small "Segoe UI";' +
							'cursor: pointer; z-index: 9999999;';
						dzSignal.onclick = function(evt) {
							for (let div of document.body.querySelectorAll('div.search-results > div.product'))
								if (div.querySelector('div.action > ul > li > a.blinking') == null) {
									div.style.display = 'none';
									decSi();
								}
						};
						document.body.append(dzSignal);
					}
				}
			}
			return status;
		}
		function setAmbiguity(n, status = 1) {
			let span = document.createElement('span');
			span.textContent = n;
			span.style.marginLeft = '5px';
			span.style.fontWeight = '900';
			a.append(span);
			return applyAlbumStatus(status);
		}

		if (results.total <= 0 || !Array.isArray(results.data)) return applyAlbumStatus(-1);
		let albums = results.data.filter(function(match) {
			if (match.release_date && parseInt(article.releaseDate) >= 0) {
				const releaseDate = new Date(match.release_date);
				if (!isNaN(releaseDate) && (/*article.releaseYear > 0 ? releaseDate.getFullYear() != article.releaseYear
						: */article.releaseDate >= 0 && releaseDate.getDateValue() != article.releaseDate)) return false;
			} else if (jaroWrinkerSimilarity(match.title, article.album) < 0.75
				|| !vaParser.test(article.artist) && jaroWrinkerSimilarity(match.artist.name, article.artist) < 0.75)
					return false;
			return true;
		});
		if (albums.length <= 0) return applyAlbumStatus(0);
		//if (albums.length > 1) return setAmbiguity(albums.length);
		let albumId;
		return Promise.all(albums.map(album => queryDeezerAPI('album', album.id).then(function(album) {
			if (article.totalTracks > 0 && album.nb_tracks > 0 && album.nb_tracks != article.totalTracks) {
				console.debug('Deezer album mismatch:', article, album);
				return 0;
			}
			albumId = album.id;
			return Array.isArray(album.tracks.data) && album.tracks.data.every(track => track.readable) ? 4 : 3;
		}, function(reason) {
			console.error(reason);
			return 2;
		}))).then(statuses => statuses.filter(status => status > 0)).then(function(matches) {
			if (matches.length <= 0) return applyAlbumStatus(0);
			if (matches.length > 1)
				return setAmbiguity(matches.length, matches.every(match => match == matches[0]) ? matches[0] : undefined);
			if (albumId) a.href = 'https://www.deezer.com/album/' + albumId;
			return applyAlbumStatus(matches[0]);
		});
	});
}

function deezerRescan(evt) {
	const dzSignal = document.getElementById('deezer-change-indicator');
	if (dzSignal != null) dzSignal.remove();
	//document.body.querySelectorAll('div.search-results > div.product').forEach(queryDeezerStatus);
	evt.target.disabled = true; //evt.target.classList.remove('enabled');
	Promise.all(Array.from(document.body.querySelectorAll('div.search-results > div.product')).map(queryDeezerStatus))
		.then(statuses => { /*evt.target.classList.add('enabled'); */evt.target.disabled = false; });
}

function scanPage(document = window.document, ids = new Set, timeFilter = false, marketCode = undefined, bypassCache = false) {
	if (!(document instanceof Document)) return Promise.reject('Invalid document');
	const titleStrippers = [
		[/(?:\s+(?:\([^\(\)]+\)|\[[^\[\]]+\]|\{[^\{\}]+\}))+\s*$/, ''],
		[/\s+(?:-\s+)?(?:EP)$/, ''],
		[/\s+-s+(?:Single)$/, ''],
		//[/\s*[\/\&\|\\\<\>\:]+\s*/g, ' '],
		[/\s*[\/\|\\\<\>]+\s*/g, ' '],
	];
	today = new Date().getDateValue();
	let promises = [ ];
	document.querySelectorAll('div.search-results > div.product').forEach(function(div) {
		let node = div.querySelector('div.album-title > a');
		if (node == null) {
			console.warn('Album title extraction failure:', div);
			return;
		}
		if (div.id = getIdFromUrl(node)) {
			if (ids.has(div.id)) return; // dupe
			ids.add(div.id);
		} else console.warn('Failed to extract release id:', div);
		node.style.width = 'fit-content';
		if (!node.style.width) node.style.width = '-moz-fit-content';
		div.album = node.textContent.trim();
		div.url = node.href;
		if (marketCode) {
			div.countryCode = marketCode = marketCode.toLowerCase();
			let img = document.createElement('img');
			img.src = '/img/icons/flags-22x14/' + marketCode.slice(0, 2) + '.svg';
			img.style = 'float: right; height: 20px;';
			const countryAliases = {
				us: 'United States',
				gb: 'United Kingdom', ch: 'Switzerland',
				fr: 'France', de: 'Germany', it: 'Italy', es: 'Spain',
				nl: 'Netherlands', be: 'Belgium', lu: 'Luxembourg', at: 'Austria', ir: 'Ireland',
				dk: 'Denmark', fi: 'Finland', no: 'Norway', se: 'Sweden',
				au: 'Australia', nz: 'New Zealand',
			};
			for (let key in countryAliases) if (marketCode.slice(0, 2) == key.toLowerCase()) {
				img.title = countryAliases[key];
				break;
			}
			node.parentNode.insertBefore(img, node);
		}
		if ((node = div.querySelector('div.artist-name > a')) == null) {
			console.warn('Album artist extraction failure:', div);
			if ((node = div.querySelector('div.artist-name')) == null) return;
		}
		div.artist = node.textContent.trim();
		if (node.nodeName == 'A') {
			node.style.width = 'fit-content';
			if (!node.style.width) node.style.width = '-moz-fit-content';
			if (!vaParser.test(div.artist)) {
				node.href = 'https://redacted.ch/artist.php?artistname=' + encodeURIComponent(div.artist);
				node.target = '_blank';
			}
		}
		if ((node = div.querySelector('span.category')) != null
				&& _QobuzGenresRating.computeQuality(div.genre = node.textContent.trim()) < 0.1) {
			if (div.parentNode != null) div.remove();
			return;
		}
		if (timeFilter) {
			node.style.cursor = 'pointer';
			node.onclick = markAllOfGenre;
			node.title = 'Mark all of the genre checked';
		}
		if ((node = div.querySelector('span.brand')) != null) div.label = node.textContent.trim();
		if (div.querySelector('div.price > div.quality-smr') != null) div.hires = true;
		div.releaseDate = getReleaseDate(div);
		div.releaseYear = getReleaseYear(div);
		if (Array.isArray(timeFilter) && !(div.releaseDate >= Math.min(...timeFilter)
			&& div.releaseDate <= Math.max(...timeFilter))) return;
		div.isProcessed = !Array.isArray(timeFilter) && (div.releaseDate < today || div.id && processedItems.has(div.id));
		if (div.isProcessed && timeFilter == true) return; 
		div.style.position = 'relative';
		if (div.isProcessed && !Array.isArray(timeFilter)) div.style.backgroundColor = processedColor;
		//redWorkers.push(safeHost.then(() => visualizeGazelleStatus(div)));

		function processJSON(meta) {
			console.assert(meta && typeof meta == 'object', "meta && typeof meta == 'object'");
			div.totalDiscs = meta.media_count || 1;
			div.totalTracks = meta.tracks_count;
			div.duration = meta.duration;
			div.hires = meta.hires;
			if (meta.label && (node = div.querySelector('span.brand')) != null) {
				let a = document.createElement('A');
				a.href = '/label/' + meta.label.slug + '/download-streaming-albums/' + meta.label.id;
				if (marketCode) a.pathname = marketCode + a.pathname;
					else if (marketExtractor.test(window.document.location.pathname)) a.pathname = RegExp.$1 + a.pathname;
				if (typeof GM_openInTab == 'function') a.onclick = function(evt) {
					if (!evt.altKey || evt.ctrlKey) return;
					GM_openInTab('https://redacted.ch/collages.php?action=search&search=' +
						encodeURIComponent(meta.label.name) + '&cats[4]=1', { active: true, insert: true, setParent: true });
					return false;
				}
				a.title = 'Browse label on Qobuz (Alt + left click to lookup label collage on RED)';
				node.parentNode.insertBefore(a, node);
				a.append(node);
			}
			if (meta.parental_warning && (node = div.querySelector('div.album-cover > a')) != null) {
				let img = document.createElement('IMG');
				img.src = 'https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Parental_Advisory_label.svg/330px-Parental_Advisory_label.svg.png';
				img.width = '32';
				img.className = 'pa-sticker';
				img.alt = 'Parental Advisory';
				node.append(img);
			}
		}

		promises.push((function() {
			if (bypassCache || !rlsCacheApi.has(div.id)) return Promise.reject('Not cached or bypass forced');
			let meta = rlsCacheApi.get(div.id);
			if (!cachedApiKeys.every(key => key in meta)) {
				console.log('API cache entry for release id', div.id, 'incomplete: throwing out');
				rlsCacheApi.delete(div.id);
				return Promise.reject('Cache entry incomplete');
			}
			console.log('Using cached record for id', div.id);
			processJSON(meta);
			return Promise.resolve(div);
		})().catch(reason => queryQobuzAPI('album/get', { album_id: div.id }).then(function(response) {
			console.log('API metadata available for release id ' + div.id + ':', response);
			div.metadata = response;
			processJSON(response);
			let json = { releaseDate: div.releaseDate };
			for (let key of cachedApiKeys) if (key in response) json[key] = response[key];
			rlsCacheApi.set(div.id, json);
			return div;
		})).catch(function(reason) {
			if (bypassCache || !rlsCacheHtml.has(div.id)) return Promise.reject('Not cached or bypass forced');
			const meta = rlsCacheHtml.get(div.id);
			for (let key in meta) if (key != 'releaseDate') div[key] = meta[key];
			return div;
		}).catch(reason => localXHR(div.url).then(function(document) {
			document.querySelectorAll('section#about > ul > li').forEach(function(li) {
				if (/\b(\d+)\s*(?:dis[ck]|disco|disque)/i.test(li.textContent)) div.totalDiscs = parseInt(RegExp.$1);
				if (/\b(\d+)\s*(?:track|pist[ae]|tracce|traccia)/i.test(li.textContent)) div.totalTracks = parseInt(RegExp.$1);
				if (/\b(?:24-Bit)\b/i.test(li.textContent)) div.hires = true;
				node = li.querySelector('span.album-about__item--duration');
				if (node != null) div.duration = timeStringToTime(node.textContent);
			});
			let img = document.querySelector('img.album-quality__icon');
			if (img != null && img.alt.endsWith('Hi-Res')) div.hires = true;
			rlsCacheHtml.set(div.id, {
				releaseDate: div.releaseDate,
				totalDiscs: div.totalDiscs,
				totalTracks: div.totalTracks,
				duration: div.duration,
				hires: div.hires,
			});
			return div;
		})).catch(reason => {
			div.notFound = true;
			return div;
		}).then(function(article) {
			if ((node = article.querySelector('ul > li.partner_offer')) != null) node.remove();
			if (article.hires && (node = article.querySelector('div.album-cover > a')) != null) {
				let img = document.createElement('IMG');
				img.src = 'https://i.ibb.co/DrPVzjs/hires-audio.jpg';
				img.className = 'hires-sticker';
				img.width = '16';
				node.append(img);
			}
			if ((node = article.querySelector('div.price-box > div.action > ul > li > a')) != null) if (Array.isArray(timeFilter)) {
				node.innerHTML = '<img class="deezer-logo" src="https://cdns-files.dzcdn.net/cache/slash/images/common/logos/deezer_light.a087061697d2a623bb90b30241da46d0.png" width="55">';
				let artist, album = titleStrippers.reduce((r, def) => r.replace(...def), article.album), searchTerm = album;
				if (!vaParser.test(article.artist)) artist = article.artist;
				if (artist) searchTerm = article.artist + ' ' + searchTerm;
				node.href = encodeURI('https://www.deezer.com/search/' + searchTerm + '/album');
				node.dzSearchTerm = 'album:"' + album + '"';
				if (artist) node.dzSearchTerm = 'artist:"' + artist + '" ' + node.dzSearchTerm;
				node.target = '_blank';
				node.classList.add('deezer-link');
				node.title = 'Deezer status unknown';
				node.parentNode.parentNode.parentNode.style.marginTop =
					article.querySelector('div.price-box > div.price > *') != null ? '5px' : '2em';
				queryDeezerStatus(article);
			} else {
				node.innerHTML = '<img class="google-logo" src="https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png" width="40">';
				let searchTerm = '"' + article.album + '"';
				if (!vaParser.test(article.artist)) searchTerm = '"' + article.artist + '" ' + searchTerm;
				node.href = 'https://www.google.com/search?q=' + encodeURIComponent(searchTerm);
				node.target = '_blank';
				node.classList.add('google-link');
				node.title = 'Search on Google';
				node.parentNode.parentNode.parentNode.style.marginTop =
					article.querySelector('div.price-box > div.price > *') != null ? '6px' : '72px';
			}

			node = article.querySelector('div.price > div.quality-lls > span:last-child')
				|| article.querySelector('div.price > div[class^="quality-"] > span:last-child');
			return node != null ? xRates.then(function(rates) {
				function getPrice(node, targetCurrency) {
					console.assert(node instanceof HTMLElement, 'node instanceof HTMLElement');
					const price = node.textContent.trim();
					if (price.includes('€')) var currency = 'EUR';
					else if (price.includes('$')) switch(marketCode) {
						case 'us-en': currency = 'USD'; break;
						case 'au-en': currency = 'AUD'; break;
						case 'nz-en': currency = 'NZD'; break;
						default: currency = 'USD';
					} else if (price.includes('£')) currency = 'GBP';
					else if (price.endsWith('Fr.')) currency = 'CHF';
					else if (price.startsWith('kr')) switch(marketCode) {
						case 'se-en': currency = 'SEK'; break;
						case 'no-en': currency = 'NOK'; break;
						case 'dk-en': currency = 'DKK'; break;
					} else if ((currency = /\b([A-Z]{3})$/.exec(price) || /^([A-Z]{3})\b/.exec(price)) != null)
						currency = currency[1];
					console.assert(currency, 'currency');
					return currency && /\d+(?:[\,\.]\d+)?/.test(price) ?
						rates.getPrice(parseFloat(RegExp.lastMatch.replace(',', '.')), currency, targetCurrency) : NaN;
				}

				if (!marketCode && (marketCode = marketExtractor.exec(window.document.location.pathname)) != null)
					marketCode = marketCode[1].toLowerCase();
				article.price = getPrice(node);
				if (targetCurrency) for (node of article.querySelectorAll('div.price > div[class^="quality"] > span:last-child')) {
					if (node.textContent.includes(targetCurrency)) continue;
					switch (targetCurrency) {
						case 'USD': if (node.textContent.startsWith('$')) continue; break;
						case 'EUR': if (node.textContent.endsWith('€')) continue; break;
						case 'GBP': if (node.textContent.startsWith('£')) continue; break;
						case 'CHF': if (node.textContent.endsWith('Fr.')) continue; break;
					}
					const price = getPrice(node, targetCurrency);
					if (price > 0) switch(targetCurrency) {
						case 'USD': node.textContent = '$' + price.toFixed(2); break;
						case 'EUR': node.textContent = price.toFixed(2).replace('.', ',') + ' €'; break;
						case 'GBP': node.textContent = '£' + price.toFixed(2); break;
						case 'CHF': node.textContent = price.toFixed(2) + ' Fr.'; break;
						case 'CZK': node.textContent = price.toFixed(2).replace('.', ',') + ' Kč'; break;
						default: node.textContent = price.toFixed(2) + ' ' + targetCurrency;
					}
				}
				return article;
			}).catch(reason => article) : Promise.resolve(article);
		}).then(function(article) {
			if (article.label && (node = article.querySelector('span.brand')) != null) {
				const labelMatch = expr => typeof expt == 'string' && article.label.toLowerCase() == expr.toLowerCase()
					|| expr instanceof RegExp && expr.test(article.label) || Array.isArray(expr) && expr.some(labelMatch);
				for (let ndx = 0; ndx < labelStyles.length; ++ndx) {
					if (!labelMatch(labelStyles[ndx][0])) continue;
					node.style = labelStyles[ndx][1];
					const className = 'label-group-' + (ndx + 1);
					if (!node.classList.contains(className)) node.classList.add(className);
					break;
				}
			}

			article.rating = computeQuality(article);
			if (isFiltered(article)) article.style.display = 'none'; else incSi();
			if (article.rating >= 1 && !vaParser.test(article.artist)) {
				if ((node = article.querySelector('div.artist-name > a')) != null) {
					node.style.fontWeight = 700;
					node.style.color = 'blue';
				}
			} else if (article.rating < 1) article.style.opacity = Math.max(article.rating, 0);

			if (article.duration > 0 || article.totalTracks > 0 || article.totalDiscs > 1) {
				if ((node = article.querySelector('div.detail > p.data.overflow')) != null) {
					let div = document.createElement('DIV'), span;
					div.className = 'totals';
					if (article.totalDiscs > 1) {
						span = document.createElement('span');
						span.className = 'release-media-count';
						span.textContent = article.totalDiscs + ' discs';
						div.append(span);
					}
					if (article.totalTracks > 0) {
						span = document.createElement('span');
						span.className = 'release-tracks-count';
						span.textContent = article.totalTracks + ' track';
						if (article.totalTracks != 1) span.textContent += 's';
						div.append(span);
					}
					if (article.duration > 0) {
						span = document.createElement('span');
						span.className = 'release-duration';
						if (article.duration > 30 * 60) span.style.fontWeight = 'bold';
						span.textContent = makeTimeString(article.duration);
						div.append(span);
					}
					node.append(div);
				}
			} else { // HTTP 404
				console.assert(article.notFound, 'article.notFound');
				if (!article.isProcessed) article.style.backgroundColor = 'darkgray';
				if ((node = article.querySelector('div.detail > div.desc.overflow > div.summary')) != null) {
					let b = node.querySelector('b');
					if (b == null) {
						b = document.createElement('B');
						node.prepend(document.createElement('BR'));
						node.prepend(b);
					}
					b.textContent = 'Not found';
					b.style.color = 'palevioletred';
				}
			}
			if (!article.style.backgroundColor) article.style.backgroundColor = 'white';
			if (article.releaseDate >= today) {
				let label = document.createElement('LABEL');
				label.className = 'reviewed checkbox-expander';
				label.title = 'Mark/unmark item as reviewed';
				let input = document.createElement('INPUT');
				input.type = 'checkbox';
				input.name = 'checked';
				input.checked = !Array.isArray(timeFilter) && article.isProcessed;
				input.style.margin = '0';
				input.onchange = function(evt) {
					article.style.backgroundColor = (article.isProcessed = evt.currentTarget.checked) ?
						processedColor : article.notFound ? 'darkgray' : 'white';
					if (removeCheckedInstantly && article.isProcessed && dynFilters.onlyUnseen) {
						article.style.display = 'none';
						decSi();
					}
					if (!Array.isArray(timeFilter) && article.id) {
						if (evt.currentTarget.checked) processedItems.set(article.id, article.releaseDate);
							else processedItems.delete(article.id);
						saveProcessedItems();
					}
				};
				label.append(input);
				article.prepend(label);
			}
			return article;
		}));
	});

	return promises;
}

function lookup(evt) {
	if (evt instanceof MouseEvent && evt.target.nodeName != 'BUTTON') return true;
	const root = document.querySelector('div.search-results');
	if (root == null) throw 'Assertion failed: invalid page structure';
	const button = evt.currentTarget;
	if (button.classList.contains('button-progress')) return false;
	if (!pathParser.test(document.location.pathname)) throw 'Unexpected path structure';
	const marketCode = RegExp.$1 || 'fr-fr', category = RegExp.$2, id = RegExp.$3;
	enableButtons(false, button);
	button.classList.add('button-progress');
	const AG = button.AG && !(button.useModifiers && evt.ctrlKey);
	const AM = button.AM && !(button.useModifiers && evt.shiftKey);
	const NCS = button.NCS && button.useModifiers && evt.altKey;
	if (lastLookup instanceof HTMLElement) {
		lastLookup.style.backgroundColor = null;
		if (lastLookup.dataset.text) lastLookup.textContent = lastLookup.dataset.text;
		lastLookup = null;
	}
	if (!button.dataset.text) button.dataset.text = button.textContent;
	button.style.backgroundColor = 'red';
	button.textContent = 'Working...';
	if (!Array.isArray(button.timeFilter) && (ref = document.getElementById('rescan-deezer')) != null)
		ref.style.display = 'none';
	onlyAvailOnDeezer = Array.isArray(button.timeFilter) && GM_getValue('auto_only_on_deezer', false);
	if ((ref = document.getElementById('dynfilter-only-on-deezer')) != null) {
		ref.checked = onlyAvailOnDeezer;
		ref.parentNode.parentNode.style.display = Array.isArray(button.timeFilter) ? null : 'none';
	}
	onlyReadOnDeezer = Array.isArray(button.timeFilter) && GM_getValue('auto_only_downloadable_from_deezer', false);
	if ((ref = document.getElementById('dynfilter-only-dl-deezer')) != null) {
		ref.checked = onlyReadOnDeezer;
		ref.parentNode.parentNode.style.display = Array.isArray(button.timeFilter) ? null : 'none';
	}
	['deezer-change-indicator', 'nextpage-clone', 'page-indicator', 'red-status-outer', 'red-status']
		.forEach(id => { if ((ref = document.getElementById(id)) != null) ref.remove() });
	if ((ref = document.body.querySelector('div.search-result-header > button.toggle-button')) != null)
		ref.remove();
	if (pageControls instanceof HTMLElement) {
		pageControls.parentNode.remove();
		pageControls = null;
	}
	for (let div of root.querySelectorAll(':scope > div.product')) root.removeChild(div);
	if (si instanceof HTMLElement) si.textContent = 0;
	let resultScanners = [ ], promises = [ ], ids = new Set; redWorkers = [ ];
	today = new Date().getDateValue();
	(AM ? markets : [marketCode]).forEach(function(marketCode) {
		(AG ? categories : [category]).forEach(function(category) {
			function loadResults(page, sortParam) {
				if (AM) button.style.background =
					`red url("/img/icons/flags-22x14/${marketCode.slice(0, 2)}.svg") center right 2pt/26pt no-repeat`;
				let caption = 'Scanning...';
				if (AG) caption += ' (' + category.replace(/^.*\//, '').replace(/-/g, ' ') + ')';
				button.textContent = caption + ` [${page || 1}/${promises.length}]`;
				let url = new URL(`/${marketCode}/${category}/download-streaming-albums`, document.location.origin);
				if (id) url.pathname += '/' + id;
				if (!Array.isArray(button.timeFilter)) { // lookup upcoming
					url.searchParams.set('f[ne]', '+'); // coming soon ("f[ne]=+")
					url.searchParams.set('s', sortParam || 'rdc'); // rdc=release date descending
				} else if (!NCS) { // lookup upcoming near day
					url.searchParams.set('f[ne]', '+'); // coming soon
					url.searchParams.set('s', 'art_dis'); // sort by most acclaimed
					//url.searchParams.set('s', 'sf'); // bestsellers
				} else { // lookup released today; alternate method
					url.searchParams.set('f[ne]', '\\-1'); // last month ('f[ne]=\-1')
					url.searchParams.set('s', 'rdc'); // sort by release date descending
				}
				if (page) url.searchParams.set('page', page);
				//url.searchParams.sort();
				return localXHR(url).then(function(document) {
					Array.prototype.push.apply(promises, scanPage(document, ids, button.timeFilter,
						AM ? marketCode : undefined, button.timeFilter == true && evt.ctrlKey));
					let ref = document.querySelector('div.search-results > div.pagination > ul > li:last-child > a');
					return ref != null && ref.textContent == '→' ? loadResults((page || 1) + 1, sortParam) : true;
				});
			}

			resultScanners.push(loadResults(1));
			if (!Array.isArray(button.timeFilter))
				resultScanners.push(loadResults(1, 'art_dis')/*, loadResults(1, 'sf')*/); // most acclaimed / bestsellers
		});
	});
	Promise.all(resultScanners).then(function(results) {
		if (AM) button.style.backgroundImage = 'none';
		button.textContent = 'Working... (loading releases details)';
		return Promise.all(promises);
	}).then(function(articles) {
		ids.clear();
		ref = articles.length;
		articles = articles.filter(function(article) {
			if (article.rating < (button['100%'] ? 1 : 0.1)) return false;
			// for (let img of div.getElementsByTagName('IMG')) // https://static-www.qobuz.com/img/redesign/blank.png
			// 	if (img.dataset.src && img.src.endsWith('/blank.png')) img.src = img.dataset.src;
			return true;
		});
		if (!Array.isArray(button.timeFilter)) articles.sort(function(a, b) {
			let cmp = b.releaseDate - a.releaseDate;
			if (isNaN(cmp)) return 0;
			if (cmp != 0) return cmp;
			cmp = b.price - a.price;
			return cmp || 0;
		});
		for (let article of articles) root.append(article); //articles.forEach(HTMLDivElement.prototype.append.bind(root));
		if (articles.length != ref) updateSi();
		button.textContent = 'Done (' + articles.length + ')';
		if (redWorkers.length > 0) Promise.all(redWorkers).then(redScanDone);
		if (Array.isArray(button.timeFilter) && (ref = document.getElementById('rescan-deezer')) != null)
			ref.style.display = null;
		button.style.backgroundColor = '#008000';
		button.classList.remove('button-progress');
		enableButtons(true, button);
	  // setTimeout(function() {
	  // button.textContent = button.dataset.text;
	  // button.style.backgroundColor = null;
	  // }, 10000);
		saveRlsCaches();
	}).catch(function(reason) {
		alert(reason);
		saveRlsCaches();
		document.location.reload();
	});
	lastLookup = button;
}

function markAllProcessed(evt) {
	if (!(lastLookup instanceof HTMLButtonElement)) return;
	const button = evt.currentTarget;
	button.disabled = true;
	if (button.hTimer) {
		clearTimeout(button.hTimer);
		delete button.hTimer;
	}
	button.textContent = 'Working...';
	button.style.backgroundColor = 'red';
	button.classList.add('button-progress');

	function forEach(callback) {
		for (let div of document.body.querySelectorAll('div.search-results > div.product'))
			if (div.style.display != 'none') callback(div);
	}

	if (Array.isArray(lastLookup.timeFilter)) {
		forEach(function(div) {
			div.isProcessed = true;
			div.style.backgroundColor = processedColor;
			if (dynFilters.onlyUnseen) div.style.display = 'none';
		});
		if (dynFilters.onlyUnseen && si instanceof HTMLElement) si.textContent = 0;
		button.textContent = 'Marked';
	} else {
		today = new Date().getDateValue();
		processedItems.forEach(function(val, key) { if (!val || val < today) processedItems.delete(key) });
		forEach(function(div) {
			const id = div.id || getIdFromUrl(div.querySelector('div.album-title > a'));
			if (!id) return console.warn('Failed to extract id from item', div); else if (!processedItems.has(id)) {
				const releaseDate = div.releaseDate || getReleaseDate(div);
				if (releaseDate < today) return;
				if (!(releaseDate >= 0)) return console.warn('Failed to extract release date from item', div);
				processedItems.set(id, releaseDate);
			}
			div.remove();
		});
		saveProcessedItems();
		if (dynFilters.onlyUnseen && si instanceof HTMLElement) si.textContent = 0;
		button.textContent = 'Saved (' + processedItems.size + ')';
	}
	button.style.backgroundColor = '#080';
	button.classList.remove('button-progress');
	button.disabled = false;
	button.hTimer = setTimeout(function(button) {
		delete button.hTimer;
		button.style.backgroundColor = null;
		button.textContent = 'All checked';
	}, 5000, button);
}

function markAllOfGenre(evt) {
	if (!(lastLookup instanceof HTMLButtonElement && evt.currentTarget instanceof HTMLSpanElement)) return;
	const genre = evt.currentTarget.textContent.trim();
	const wasChecked = evt.currentTarget.parentNode.parentNode.parentNode.isProcessed;
	if (!wasChecked && !evt.ctrlKey && !confirm('Sure to mark all of genre ' + genre + ' as checked?')) return;

	function forEach(callback) {
		for (let div of document.body.querySelectorAll('div.search-results > div.product')) {
			if (div.style.display == 'none') continue;
			let category = div.querySelector('span.category');
			if (category != null && category.textContent.trim().toLowerCase() == genre.toLowerCase()) callback(div);
		}
	}

	if (Array.isArray(lastLookup.timeFilter)) {
		forEach(function(div) {
			if (div.isProcessed = !wasChecked) {
				div.style.backgroundColor = processedColor;
				if (!dynFilters.onlyUnseen) return;
				div.style.display = 'none';
				decSi();
			} else div.style.backgroundColor = div.notFound ? 'darkgray' : 'white';
		});
	} else {
		forEach(function(div) {
			const id = div.id || getIdFromUrl(div.querySelector('div.album-title > a'));
			if (!id) return console.warn('Failed to extract id from item', div);
			const releaseDate = div.releaseDate || getReleaseDate(div);
			if (releaseDate < today) return;
			if (!(releaseDate >= 0)) return console.warn('Failed to extract release date from item', div);
			if (div.isProcessed = !wasChecked) {
				if (!processedItems.has(id)) processedItems.set(id, releaseDate);
				div.style.backgroundColor = processedColor;
				if (!dynFilters.onlyUnseen) return;
				div.style.display = 'none';
				decSi();
			} else {
				if (processedItems.has(id)) processedItems.delete(id, releaseDate);
				div.style.backgroundColor = div.notFound ? 'darkgray' : 'white';
			}
		});
		saveProcessedItems();
	}
}