/*
 * @web-scrobbler/metadata-filter v3.2.0
 * (c) Web Scrobbler MetadataFilter Team
 * Licensed under the MIT License
 */
(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
	typeof define === 'function' && define.amd ? define(['exports'], factory) :
	(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MetadataFilter = {}));
})(this, (function (exports) { 'use strict';

	/**
	 * Assert the given fields are valid.
	 *
	 * @param fields Fields to filter
	 *
	 * @throws Throw an error if the assertion is failed
	 */
	function assertFieldsAreValid(fields) {
	    if (!Array.isArray(fields)) {
	        throw new TypeError(`Invalid 'fields' argument: expected 'string[]', got '${typeof fields}'`);
	    }
	    if (fields.length === 0) {
	        throw new Error("Invalid 'fields' argument: received an empty array");
	    }
	    for (const field of fields) {
	        if (typeof field !== 'string') {
	            throw new TypeError(`Invalid field: expected 'string', got '${typeof field}'`);
	        }
	        if (!field) {
	            throw new TypeError("Invalid field: expected 'string', got an empty string");
	        }
	    }
	}
	/**
	 * Assert the given object is a valid filter set.
	 *
	 * @param filterSet Filter set
	 *
	 * @throws Throw an error if the assertion is failed
	 */
	function assertFilterSetIsValid(filterSet) {
	    if (!filterSet) {
	        throw new TypeError('No filter set is specified!');
	    }
	    if (typeof filterSet !== 'object') {
	        throw new TypeError(`Invalid filter set: expected 'object', got '${typeof filterSet}'`);
	    }
	    for (const field in filterSet) {
	        assertFilterSetFiltersAreValid(filterSet[field]);
	    }
	}
	/**
	 * Assert the given filter function or the array of filter functions are valid.
	 *
	 * @param filterFn Filter function or array of filter functions
	 *
	 * @throws Throw an error if the assertion is failed
	 */
	function assertFilterSetFiltersAreValid(filterFn) {
	    if (Array.isArray(filterFn)) {
	        assertArrayOfFilterFunctionsIsValid(filterFn);
	    }
	    else {
	        assertFilterFunctionIsValid(filterFn);
	    }
	}
	/**
	 * Assert each function in the given array of filter functions is valid.
	 *
	 * @param filterFnArr Array of filter functions
	 *
	 * @throws Throw an error if the assertion is failed
	 */
	function assertArrayOfFilterFunctionsIsValid(filterFnArr) {
	    filterFnArr.forEach(assertFilterFunctionIsValid);
	}
	/**
	 * Assert the given filter function is valid.
	 *
	 * @param filterFn Filter function
	 *
	 * @throws Throw an error if the assertion is failed
	 */
	function assertFilterFunctionIsValid(filterFn) {
	    if (typeof filterFn !== 'function') {
	        throw new TypeError(`Invalid filter function: expected 'function', got '${typeof filterFn}'`);
	    }
	}

	/**
	 * Create a new MetadataFilter instance from a given filter set.
	 *
	 * @param filterSet Filter set
	 *
	 * @return MetadataFilter instance
	 */
	function createFilter(filterSet) {
	    return new MetadataFilter(filterSet);
	}
	/**
	 * Create a filter set where each given field has given filter function(s).
	 * Useful to create a filter where multiple fields should be filtered with
	 * the same filter functions.
	 *
	 * @param fields Array of fields to filter
	 * @param filterFn Filter function or array of filter functions
	 *
	 * @return Filter set object
	 */
	function createFilterSetForFields(fields, filterFn) {
	    assertFieldsAreValid(fields);
	    assertFilterSetFiltersAreValid(filterFn);
	    return fields.reduce((acc, field) => {
	        acc[field] = filterFn;
	        return acc;
	    }, {});
	}
	/**
	 * Base filter object that filters metadata fields by given filter set.
	 *
	 * The filter set is an object containing properties (fields) with filter
	 * functions. Each field can contain a single filter function, or an array of
	 * filter functions.
	 *
	 * The filter function is a pure function which takes a non-empty string and
	 * returns a modified string.
	 *
	 * These filter functions will be applied to a field value passed in
	 * `MetadataFilter.filterField` method.
	 */
	class MetadataFilter {
	    /**
	     * @constructor
	     *
	     * @param filterSet Set of filters
	     *
	     * @throws Throw an error if no filter set is specified
	     */
	    constructor(filterSet) {
	        this.mergedFilterSet = {};
	        this.appendFilters(filterSet);
	    }
	    /**
	     * Filter the field value using filters for the given field.
	     *
	     * @param field Metadata field
	     * @param fieldValue Field value to be filtered
	     *
	     * @return Filtered string
	     *
	     * @throws Throw an error if an invalid field is specified
	     */
	    filterField(field, fieldValue) {
	        if (field in this.mergedFilterSet) {
	            return this.filterText(fieldValue, this.mergedFilterSet[field]);
	        }
	        throw new TypeError(`Invalid filter field: ${field}`);
	    }
	    /**
	     * Append a new filter set.
	     *
	     * @param filterSet Set of filters
	     *
	     * @return Current instance
	     */
	    append(filterSet) {
	        this.appendFilters(filterSet);
	        return this;
	    }
	    /**
	     * Extend the filter by a filter set from a given filter.
	     *
	     * @param filter Filter object
	     *
	     * @return Current instance
	     */
	    extend(filter) {
	        if (!filter) {
	            throw new TypeError('No filter is specified!');
	        }
	        if (!(filter instanceof MetadataFilter)) {
	            throw new TypeError(`Invalid filter: expected 'MetadataFilter', got '${typeof filter}'`);
	        }
	        this.appendFilters(filter.mergedFilterSet);
	        return this;
	    }
	    /**
	     * Check if the filter contains filter functions for a given field.
	     *
	     * @param field Field to check
	     *
	     * @return Check result
	     */
	    canFilterField(field) {
	        return field in this.mergedFilterSet;
	    }
	    /**
	     * Return a list of fields that the filter can filter.
	     *
	     * @return List of fields
	     */
	    getFields() {
	        return Object.keys(this.mergedFilterSet);
	    }
	    /**
	     * Filter text using given filters.
	     *
	     * @param text String to be filtered
	     * @param filters Array of filter functions
	     *
	     * @return Filtered string
	     */
	    filterText(text, filters) {
	        if (!text) {
	            return text;
	        }
	        return filters.reduce((text, filter) => filter(text), text);
	    }
	    /**
	     * Wrap given filters into array of filters, if needed.
	     *
	     * @param filters Array of filter functions or filter function
	     *
	     * @return Array of filter functions
	     */
	    wrapFiltersIntoArray(filters) {
	        if (Array.isArray(filters)) {
	            return filters;
	        }
	        const filterFn = filters;
	        return [filterFn];
	    }
	    /**
	     * Add given filters to current ones.
	     *
	     * @param filterSet Set of filters
	     *
	     * @throws Throw an error if a filter function is not a function
	     */
	    appendFilters(filterSet) {
	        assertFilterSetIsValid(filterSet);
	        for (const field in filterSet) {
	            if (!(field in this.mergedFilterSet)) {
	                this.mergedFilterSet[field] = [];
	            }
	            const filterFunctions = this.wrapFiltersIntoArray(filterSet[field]);
	            this.mergedFilterSet[field].push(...filterFunctions);
	        }
	    }
	}

	/**
	 * Filter rules are an array that contains replacement rules.
	 */
	const CLEAN_EXPLICIT_FILTER_RULES = [
	    // (Explicit) or [Explicit]
	    { source: /\s[([]Explicit[)\]]/i, target: '' },
	    // (Clean) or [Clean]
	    { source: /\s[([]Clean[)\]]/i, target: '' },
	];
	const FEATURE_FILTER_RULES = [
	    // [Feat. Artist] or (Feat. Artist)
	    { source: /\s[([]feat. .+[)\]]/i, target: '' },
	    { source: /\s(feat. .+)/i, target: '' },
	];
	const LIVE_FILTER_RULES = [
	    // Track - Live
	    // Track - Live at
	    { source: /\s-\sLive(\s.+)?$/, target: '' },
	    // Track (Live)
	    { source: /\s[([]Live[)\]]$/, target: '' },
	];
	const NORMALIZE_FEATURE_FILTER_RULES = [
	    // [Feat. Artist] or (Feat. Artist) -> Feat. Artist
	    { source: /\s[([](feat. .+)[)\]]/i, target: ' $1' },
	];
	const PARODY_FILTER_RULES = [
	    // Party In the CIA (Parody of "Party In The U.S.A." by Miley Cyrus)
	    { source: /\s\(Parody of ".*" by .*\)$/, target: '' },
	    // White & Nerdy (Parody of "Ridin'" by Chamillionaire feat. Krayzie Bone)
	    { source: /\s\(Parody of ".*" by .* feat\. .*\)$/, target: '' },
	    // The Saga Begins (Lyrical Adaption of "American Pie")
	    { source: /\s\(Lyrical Adaption of ".*"\)$/, target: '' },
	];
	const REISSUE_FILTER_RULES = [
	    // Album Title Re-issue
	    { source: /\sRe-?issue$/i, target: '' },
	    // Album Title [Whatever Re-issue Whatever]
	    { source: /\s\[.*?Re-?issue.*?\]/i, target: '' },
	    // Album Title (Whatever Re-issue Whatever)
	    { source: /\s\(.*?Re-?issue.*?\)/i, target: '' },
	];
	/**
	 * Filter rules to remove "Remastered..."-like strings from a text.
	 */
	const REMASTERED_FILTER_RULES = [
	    // Ticket To Ride - Live / Remastered
	    { source: /Live\s\/\sRemastered/, target: 'Live' },
	    // Mothership (Remastered)
	    // Let It Be (Remastered 2009)
	    // How The West Was Won [Remastered]
	    // Ride the Lightning (Deluxe Remaster)
	    // ...And Justice For All (Remastered Deluxe Box Set)
	    { source: /\s[([].*Re-?[Mm]aster(ed)?.*[)\]]$/, target: '' },
	    // Outside The Wall - 2011 - Remaster
	    // China Grove - 2006 Remaster
	    // Easy Living - 2003 Remastered
	    // Learning To Fly - 2001 Digital Remaster
	    // Red Right Hand - 2011 Remastered Version
	    { source: /\s-\s\d{4}(\s-)?\s.*Re-?[Mm]aster(ed)?.*$/, target: '' },
	    // Here Comes The Sun - Remastered
	    // 1979 - Remastered 2012
	    // 1979 - Remastered Version
	    { source: /\s-\sRe-?[Mm]aster(ed)?.*$/, target: '' },
	    // Wish You Were Here [Remastered] (Remastered Version)
	    { source: /\s\[Remastered\]\s\(Remastered\sVersion\)$/, target: '' },
	];
	const SUFFIX_FILTER_RULES = [
	    // "- X Remix" -> "(X Remix)" and similar
	    {
	        source: /-\s(.+?)\s((Re)?mix|edit|dub|mix|vip|version)$/i,
	        target: '($1 $2)',
	    },
	    { source: /-\s(Remix|VIP|Instrumental)$/i, target: '($1)' },
	];
	/**
	 * Special filter rules to remove leftovers after filtering text using
	 * `YOUTUBE_TRACK_FILTER_RULES` filter rules.
	 */
	const TRIM_SYMBOLS_FILTER_RULES = [
	    // Leftovers after e.g. (official video)
	    { source: /\(+\s*\)+/, target: '' },
	    // trim starting white chars and dash
	    { source: /^[/,:;~\s"-]+/, target: '' },
	    // trim trailing white chars and dash
	    { source: /[/,:;~\s"-]+$/, target: '' },
	    // remove multiple spaces
	    { source: /\u0020{1,}/, target: ' ' },
	];
	/**
	 * Filter to trim text containing "Various Artists" to just various
	 * artists. Useful for album artist on last.fm where anything more is
	 * rejected.
	 */
	const VARIOUS_ARTISTS_FILTER_RULES = [
	    { source: /(Various Artists).+/, target: '$1' },
	];
	/**
	 * Filter rules to remove "(Album|Stereo|Mono Version)"-like strings
	 * from a text.
	 */
	const VERSION_FILTER_RULES = [
	    // Love Will Come To You (Album Version)
	    { source: /\s[([]Album Version[)\]]$/, target: '' },
	    // I Melt With You (Rerecorded)
	    // When I Need You [Re-Recorded]
	    { source: /\s[([]Re-?recorded[)\]]$/, target: '' },
	    // Your Cheatin' Heart (Single Version)
	    { source: /\s[([]Single Version[)\]]$/, target: '' },
	    // All Over Now (Edit)
	    { source: /\s[([]Edit[)\]]$/, target: '' },
	    // (I Can't Get No) Satisfaction - Mono Version
	    { source: /\s-\sMono Version$/, target: '' },
	    // Ruby Tuesday - Stereo Version
	    { source: /\s-\sStereo Version$/, target: '' },
	    // Pure McCartney (Deluxe Edition)
	    { source: /\s\(Deluxe Edition\)$/, target: '' },
	    // Ace of Spades (Expanded Edition)
	    // Overkill (Expanded Bonus Track Edition)
	    // On Parole (Expanded and Remastered)
	    { source: /\s[([]Expanded.*[)\]]$/, target: '' },
	    // Sound of White Noise - Expanded Edition
	    { source: /\s-\sExpanded Edition$/, target: '' },
	    // 6 Foot 7 Foot (Explicit Version)
	    { source: /\s[([]Explicit Version[)\]]/i, target: '' },
	    // No Remorse (Bonus Track Edition)
	    { source: /\s[([]Bonus Track Edition[)\]]/i, target: '' },
	    // Peace Sells...But Who's Buying (25th Anniversary)
	    // Persistence of Time (30th Anniversary Remaster)
	    { source: /\s[([]\d+th\sAnniversary.*[)\]]/i, target: '' },
	    // 6 Foot 7 Foot - Original
	    { source: /\s-\sOriginal$/i, target: '' },
	    // California Love - Original Version
	    // Personal Jesus - Original Single Version
	    // Prince of the Moment - Original 7" Version
	    // YMCA - Original Version 1978
	    { source: /\s-\sOriginal.*Version(\s\d{4})?$/i, target: '' },
	];
	/**
	 * Filter rules to remove YouTube suffixes and prefixes from a text.
	 */
	const YOUTUBE_TRACK_FILTER_RULES = [
	    // Trim whitespaces
	    { source: /^\s+|\s+$/g, target: '' },
	    // **NEW**
	    { source: /\*+\s?\S+\s?\*+$/, target: '' },
	    // [Whatever]
	    { source: /\[[^\]]+\]/, target: '' },
	    // 【Whatever】
	    { source: /【[^\]]+】/, target: '' },
	    // （Whatever）
	    { source: /（[^\]]+）/, target: '' },
	    // (Whatever Version)
	    { source: /\([^)]*version\)$/i, target: '' },
	    // Video extensions
	    { source: /\.(avi|wmv|mpg|mpeg|flv)$/i, target: '' },
	    // (Lyrics Video)
	    { source: /\(.*lyrics?\s*(video)?\)/i, target: '' },
	    // ((Official)? (Track)? Stream)
	    { source: /\((of+icial\s*)?(track\s*)?stream\)/i, target: '' },
	    // ((Official)? (Music)? Video|Audio)
	    { source: /\((of+icial\s*)?(music\s*)?(video|audio)\)/i, target: '' },
	    // - (Official)? (Music)? Video|Audio
	    { source: /-\s(of+icial\s*)?(music\s*)?(video|audio)$/i, target: '' },
	    // ((Whatever)? Album Track)
	    { source: /\(.*Album\sTrack\)/i, target: '' },
	    // (Official)
	    { source: /\(\s*of+icial\s*\)/i, target: '' },
	    // (1999)
	    { source: /\(\s*[0-9]{4}\s*\)/i, target: '' },
	    // (HD) / (HQ)
	    { source: /\(\s*(HD|HQ)\s*\)$/, target: '' },
	    // HD / HQ
	    { source: /(HD|HQ)\s?$/, target: '' },
	    // Video Clip Officiel / Video Clip Official
	    { source: /(vid[\u00E9e]o)?\s?clip\sof+ici[ae]l/i, target: '' },
	    // Offizielles
	    { source: /of+iziel+es\s*video/i, target: '' },
	    // Video Clip
	    { source: /vid[\u00E9e]o\s?clip/i, target: '' },
	    // Clip
	    { source: /\sclip/i, target: '' },
	    // Full Album
	    { source: /full\s*album/i, target: '' },
	    // (Live)
	    { source: /\(live.*?\)$/i, target: '' },
	    // | Something
	    { source: /\|.*$/i, target: '' },
	    // Artist - The new "Track title" featuring someone
	    { source: /^(|.*\s)"(.{5,})"(\s.*|)$/, target: '$2' },
	    // 'Track title'
	    { source: /^(|.*\s)'(.{5,})'(\s.*|)$/, target: '$2' },
	    // (*01/01/1999*)
	    { source: /\(.*[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{2,4}.*\)/i, target: '' },
	    // Sub Español
	    { source: /sub\s*español/i, target: '' },
	    // (Letra)
	    { source: /\s\(Letra\)/i, target: '' },
	    // (En vivo)
	    { source: /\s\(En\svivo\)/i, target: '' },
	    // Sub Español
	    { source: /sub\s*español/i, target: '' },
	];
	const ADDITIONAL_ARTISTS_FILTER_RULES = [
	    { source: /\s(& .+)/i, target: '' },
	    { source: /\s(x .+)/i, target: '' },
	];

	const escapeHtmlEntityMap = {
	    '&': /&amp;/g,
	    '<': /&lt;/g,
	    '>': /&gt;/g,
	    '"': /&quot;/g,
	};
	/**
	 * Generate Album Artist from Artist when "feat. Artist B" is present.
	 *
	 * @param text String to be filtered
	 *
	 * @return Transformed string
	 */
	function albumArtistFromArtist(text) {
	    if (text.includes(' feat. ')) {
	        return text.split(' feat. ')[0];
	    }
	    return text;
	}
	/**
	 * Decode HTML entities in given text string.
	 *
	 * @param text String with HTML entities
	 *
	 * @return Decoded string
	 */
	function decodeHtmlEntities(text) {
	    let filteredText = text;
	    for (const target in escapeHtmlEntityMap) {
	        const source = escapeHtmlEntityMap[target];
	        filteredText = filteredText.replace(source, target);
	    }
	    filteredText = filteredText.replace(/&#x([a-fA-f0-9]+);/g, (_, hex) => {
	        const dec = parseInt(hex, 16);
	        return String.fromCharCode(dec);
	    });
	    filteredText = filteredText.replace(/&#(\d+);/g, (_, dec) => {
	        return String.fromCharCode(dec);
	    });
	    return filteredText;
	}
	/**
	 * Replace text according to given filter rules.
	 *
	 * @param text String to be filtered
	 * @param filterRules Array of replace rules
	 *
	 * @return Filtered string
	 */
	function filterWithFilterRules(text, filterRules) {
	    return filterRules.reduce((text, filterRule) => {
	        const { source, target } = filterRule;
	        return text.replace(source, target);
	    }, text);
	}
	/**
	 * Replace "Title - X Remix" suffix with "Title (X Remix) and similar".
	 *
	 * @param text String to be filtered
	 *
	 * @return Filtered string
	 */
	function fixTrackSuffix(text) {
	    return filterWithFilterRules(text, SUFFIX_FILTER_RULES);
	}
	/**
	 * Replace "Various Artists something goes here" with "Various Artists".
	 *
	 * @param text String to be filtered
	 *
	 * @return Filtered string
	 */
	function fixVariousArtists(text) {
	    return filterWithFilterRules(text, VARIOUS_ARTISTS_FILTER_RULES);
	}
	/**
	 * Generate normalized "feat. Artist B" text from [feat. Artist B] style.
	 *
	 * @param text String to be filtered
	 *
	 * @return Transformed string
	 */
	function normalizeFeature(text) {
	    return filterWithFilterRules(text, NORMALIZE_FEATURE_FILTER_RULES);
	}
	/**
	 * Remove "Explicit" and "Clean"-like strings from the text.
	 *
	 * @param text String to be filtered
	 *
	 * @return Filtered string
	 */
	function removeCleanExplicit(text) {
	    return filterWithFilterRules(text, CLEAN_EXPLICIT_FILTER_RULES);
	}
	/**
	 * Remove "feat"-like strings from the text.
	 *
	 * @param text String to be filtered
	 *
	 * @return Filtered string
	 */
	function removeFeature(text) {
	    return filterWithFilterRules(text, FEATURE_FILTER_RULES);
	}
	/**
	 * Remove "&" and "x"-like strings from the text.
	 *
	 * @param text String to be filtered
	 *
	 * @return Filtered string
	 */
	function removeAdditionalArtists(text) {
	    return filterWithFilterRules(text, ADDITIONAL_ARTISTS_FILTER_RULES);
	}
	/**
	 * Remove "Live..."-like strings from the text.
	 *
	 * @param text String to be filtered
	 *
	 * @return Filtered string
	 */
	function removeLive(text) {
	    return filterWithFilterRules(text, LIVE_FILTER_RULES);
	}
	/**
	 * Remove "(Parody of "X" by Y)"-like strings from the text.
	 * @param text String to be filtered
	 *
	 * @return Filtered string
	 */
	function removeParody(text) {
	    return filterWithFilterRules(text, PARODY_FILTER_RULES);
	}
	function removeReissue(text) {
	    return filterWithFilterRules(text, REISSUE_FILTER_RULES);
	}
	/**
	 * Remove "Remastered..."-like strings from the text.
	 *
	 * @param text String to be filtered
	 *
	 * @return Filtered string
	 */
	function removeRemastered(text) {
	    return filterWithFilterRules(text, REMASTERED_FILTER_RULES);
	}
	/**
	 * Remove "(Single|Album|Mono version}"-like strings from the text.
	 *
	 * @param text String to be filtered
	 *
	 * @return Filtered string
	 */
	function removeVersion(text) {
	    return filterWithFilterRules(text, VERSION_FILTER_RULES);
	}
	/**
	 * Remove zero-width characters from given string.
	 *
	 * @param text String to be filtered
	 *
	 * @return Filtered string
	 */
	function removeZeroWidth(text) {
	    return text.replace(/[\u200B-\u200D\uFEFF]/g, '');
	}
	/**
	 * Replace all non-breaking space symbols with a space symbol.
	 *
	 * @param text String to be filtered
	 *
	 * @return Filtered string
	 */
	function replaceNbsp(text) {
	    return text.replace(/\u00a0/g, '\u0020');
	}
	/**
	 * Remove Youtube-related garbage from the text.
	 *
	 * @param text String to be filtered
	 *
	 * @return Filtered string
	 */
	function youtube(text) {
	    return filterWithFilterRules(text, [
	        ...YOUTUBE_TRACK_FILTER_RULES,
	        ...TRIM_SYMBOLS_FILTER_RULES,
	    ]);
	}
	/**
	 * Replace smart quotes with regular quotes.
	 *
	 * @param text String to be filtered
	 *
	 * @return Filtered string
	 */
	function replaceSmartQuotes(text) {
	    return text.replace(/[\u2018\u2019]/g, "'").replace(/[\u201c\u201d]/g, '"');
	}

	/**
	 * Get a filter with YouTube-related filter functions.
	 *
	 * @return Filter object
	 */
	function createYouTubeFilter() {
	    return new MetadataFilter({ track: youtube });
	}
	/**
	 * Get a filter that removes "Remastered"-like suffixes.
	 *
	 * @return Filter object
	 */
	function createRemasteredFilter() {
	    return new MetadataFilter({
	        track: removeRemastered,
	        album: removeRemastered,
	    });
	}
	/**
	 * Get a filter with Spotify-related filter functions.
	 *
	 * @return Filter object
	 */
	function createSpotifyFilter() {
	    return new MetadataFilter({
	        track: [removeRemastered, removeParody, fixTrackSuffix, removeLive],
	        album: [
	            removeRemastered,
	            fixTrackSuffix,
	            removeLive,
	            removeReissue,
	            removeVersion,
	        ],
	    });
	}
	/**
	 * Get a filter with Amazon-related filter functions.
	 *
	 * @return Filter object
	 */
	function createAmazonFilter() {
	    return new MetadataFilter({
	        artist: [normalizeFeature],
	        track: [
	            removeCleanExplicit,
	            removeFeature,
	            removeRemastered,
	            fixTrackSuffix,
	            removeVersion,
	            removeLive,
	        ],
	        album: [
	            decodeHtmlEntities,
	            removeCleanExplicit,
	            removeRemastered,
	            fixTrackSuffix,
	            removeVersion,
	            removeLive,
	        ],
	        albumArtist: [normalizeFeature, albumArtistFromArtist],
	    });
	}
	/**
	 * Get a filter with Tidal-related filter functions.
	 *
	 * @return Filter object
	 */
	function createTidalFilter() {
	    return new MetadataFilter({
	        track: [removeRemastered, fixTrackSuffix, removeVersion, removeLive],
	        album: [removeRemastered, fixTrackSuffix, removeVersion, removeLive],
	    });
	}

	exports.MetadataFilter = MetadataFilter;
	exports.albumArtistFromArtist = albumArtistFromArtist;
	exports.createAmazonFilter = createAmazonFilter;
	exports.createFilter = createFilter;
	exports.createFilterSetForFields = createFilterSetForFields;
	exports.createRemasteredFilter = createRemasteredFilter;
	exports.createSpotifyFilter = createSpotifyFilter;
	exports.createTidalFilter = createTidalFilter;
	exports.createYouTubeFilter = createYouTubeFilter;
	exports.decodeHtmlEntities = decodeHtmlEntities;
	exports.filterWithFilterRules = filterWithFilterRules;
	exports.fixTrackSuffix = fixTrackSuffix;
	exports.fixVariousArtists = fixVariousArtists;
	exports.normalizeFeature = normalizeFeature;
	exports.removeAdditionalArtists = removeAdditionalArtists;
	exports.removeCleanExplicit = removeCleanExplicit;
	exports.removeFeature = removeFeature;
	exports.removeLive = removeLive;
	exports.removeParody = removeParody;
	exports.removeReissue = removeReissue;
	exports.removeRemastered = removeRemastered;
	exports.removeVersion = removeVersion;
	exports.removeZeroWidth = removeZeroWidth;
	exports.replaceNbsp = replaceNbsp;
	exports.replaceSmartQuotes = replaceSmartQuotes;
	exports.youtube = youtube;

	Object.defineProperty(exports, '__esModule', { value: true });

}));
