Repository: beetbox/beets

Issue:
scrub: The `auto` option should be a no-op when `import.write` is disabled
### Problem

I was importing new tracks with `import.write` disabled and seeing all tracks populated with new tags. With help from the [forum](https://discourse.beets.io/t/tags-are-being-written-to-imported-files-even-though-import-write-no/2068/4), I was able to determine that with `scrub.auto` enabled and `import.write` disabled, the scrub plugin removes tags and rewrites them from the database.
The [scrub documentation](https://beets.readthedocs.io/en/stable/plugins/scrub.html#automatic-scrubbing) indicates that scrubbing on import is only active if `import.write` is also enabled.
> When importing new files (with import.write turned on) or modifying files’ tags with the beet modify command, beets will first strip all types of tags entirely and then write the database-tracked metadata to the file.

I'd think a resolution could be:
- Update the documentation to indicate scrub doesn't read the `import.write` setting.
- Or scrub _should_ read that setting.
- Or, even better, have a separate scrub configuration that disables restoring tags after the scrub.

### Setup

* OS: Docker https://hub.docker.com/r/linuxserver/beets
* Python version: 3.9.7
* beets version: 1.6.0
* Turning off plugins made problem go away (yes/no): yes

My configuration (output of `beet config`) is:

```yaml
lyrics:
    bing_lang_from: []
    auto: no
    fallback: ''
    sources: genius
    bing_client_secret: REDACTED
    bing_lang_to:
    google_API_key: REDACTED
    google_engine_ID: REDACTED
    genius_api_key: REDACTED
    force: no
    local: no

plugins:
- fetchart
- embedart
- convert
- scrub
- replaygain
- lyrics
- lastgenre
- the
- inline
- chroma
- web
- permissions
directory: /music
library: /config/musiclibrary.blb
art_filename: _cover
threaded: yes
original_date: yes
per_disc_numbering: yes
chroma:
    auto: yes
embedart:
    auto: no
    maxwidth: 0
    compare_threshold: 0
    ifempty: no
    remove_art_file: no
    quality: 0
convert:
    auto: no
    dest: /converted
    format: aac
    formats:
        aac:
            command: ffmpeg -i $source -c:a aac -vbr 4 $dest
            extension: m4a
        mp3:
            command: /config/gapless-mp3.sh $source $dest
            extension: mp3
        alac:
            command: ffmpeg -i $source -y -vn -acodec alac $dest
            extension: m4a
        flac: ffmpeg -i $source -y -vn -acodec flac $dest
        opus: ffmpeg -i $source -y -vn -acodec libopus -ab 96k $dest
        ogg: ffmpeg -i $source -y -vn -acodec libvorbis -aq 3 $dest
        wma: ffmpeg -i $source -y -vn -acodec wmav2 -vn $dest
    pretend: no
    link: no
    hardlink: no
    threads: 8
    id3v23: inherit
    max_bitrate: 500
    tmpdir:
    quiet: no
    embed: yes

    paths: {}
    no_convert: ''
    never_convert_lossy_files: no
    copy_album_art: no
    album_art_maxwidth: 0
    delete_originals: no
item_fields:
    disc_and_track: u'%02i.%02i' % (disc, track) if disctotal > 1 else u'%02i' % (track)
the:
    a: no
    the: yes
    format: '{0}, {1}'
    strip: no
    patterns: []

paths:
    default: '%the{$albumartist}/$original_year - $album%aunique{}/$disc_and_track $title'
    singleton: '%the{$albumartist}/_singles/$original_year - $title'
    comp: _Compilations/$original_year - $album%aunique{}/$disc_and_track $title
    albumtype_soundtrack: _Soundtracks/$original_year - $album%aunique{}/$disc_and_track $title
permissions:
    file: 664
    dir: 775

import:
    write: no
    copy: no
    move: yes
    resume: ask
    incremental: no
    quiet_fallback: skip
    timid: no
    log: /config/beet.log
lastgenre:
    auto: yes
    source: album
    whitelist: yes
    min_weight: 10
    count: 1
    fallback:
    canonical: no
    force: yes
    separator: ', '
    prefer_specific: no
    title_case: yes
fetchart:
    auto: yes
    store_source: yes
    minwidth: 600
    sources:
    - filesystem
    - coverart
    - itunes
    - albumart
    - amazon
    maxwidth: 0
    quality: 0
    max_filesize: 0
    enforce_ratio: no
    cautious: no
    cover_names:
    - cover
    - front
    - art
    - album
    - folder
    google_key: REDACTED
    google_engine: 001442825323518660753:hrh5ch1gjzm
    fanarttv_key: REDACTED
    lastfm_key: REDACTED
    high_resolution: no
    deinterlace: no
    cover_format:
replaygain:
    auto: yes
    backend: ffmpeg
    overwrite: no
    threads: 8
    parallel_on_import: no
    per_disc: no
    peak: 'true'
    targetlevel: 89
    r128: [Opus]
    r128_targetlevel: 84
scrub:
    auto: yes

replace:
    ^\.: _
    '[\x00-\x1f]': _
    '[<>:"\?\*\|]': _
    '[\xE8-\xEB]': e
    '[\xEC-\xEF]': i
    '[\xE2-\xE6]': a
    '[\xF2-\xF6]': o
    '[\xF8]': o
    \.$: _
    \s+$: ''
web:
    host: 0.0.0.0
    port: 8337
    cors: ''
    cors_supports_credentials: no
    reverse_proxy: no
    include_paths: no
    readonly: yes
pathfields: {}
album_fields: {}

```


Discussion:

Patch:
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index a4ee45e6a..5fde4b961 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -288,7 +288,7 @@ Other ways to run the tests:
 You can also see the latest test results on `Linux`_ and on `Windows`_.
 
 Note, if you are on Windows and are seeing errors running tox, it may be related to `this issue`_,
-in which case you may have to install tox v3.8.3 e.g. ``python -m pip install tox==3.8.3``
+in which case you may have to install tox v3.8.3 e.g. ``python -m pip install tox=3.8.3``
 
 .. _this issue: https://github.com/tox-dev/tox/issues/1550
 
diff --git a/beets/autotag/__init__.py b/beets/autotag/__init__.py
index 59b62385f..339e3826c 100644
--- a/beets/autotag/__init__.py
+++ b/beets/autotag/__init__.py
@@ -41,7 +41,6 @@ SPECIAL_FIELDS = {
         'va',
         'releasegroup_id',
         'artist_id',
-        'artists_ids',
         'album_id',
         'mediums',
         'tracks',
@@ -49,28 +48,21 @@ SPECIAL_FIELDS = {
         'month',
         'day',
         'artist',
-        'artists',
         'artist_credit',
-        'artists_credit',
         'artist_sort',
-        'artists_sort',
         'data_url'
     ),
     'track': (
         'track_alt',
         'artist_id',
-        'artists_ids',
         'release_track_id',
         'medium',
         'index',
         'medium_index',
         'title',
         'artist_credit',
-        'artists_credit',
         'artist_sort',
-        'artists_sort',
         'artist',
-        'artists',
         'track_id',
         'medium_total',
         'data_url',
@@ -85,18 +77,13 @@ def apply_item_metadata(item: Item, track_info: TrackInfo):
     """Set an item's metadata from its matched TrackInfo object.
     """
     item.artist = track_info.artist
-    item.artists = track_info.artists
     item.artist_sort = track_info.artist_sort
-    item.artists_sort = track_info.artists_sort
     item.artist_credit = track_info.artist_credit
-    item.artists_credit = track_info.artists_credit
     item.title = track_info.title
     item.mb_trackid = track_info.track_id
     item.mb_releasetrackid = track_info.release_track_id
     if track_info.artist_id:
         item.mb_artistid = track_info.artist_id
-    if track_info.artists_ids:
-        item.mb_artistids = track_info.artists_ids
 
     for field, value in track_info.items():
         # We only overwrite fields that are not already hardcoded.
@@ -121,34 +108,21 @@ def apply_metadata(album_info: AlbumInfo, mapping: Mapping[Item, TrackInfo]):
                            track_info.artist or
                            album_info.artist_credit or
                            album_info.artist)
-            item.artists = (track_info.artists_credit or
-                            track_info.artists or
-                            album_info.artists_credit or
-                            album_info.artists)
             item.albumartist = (album_info.artist_credit or
                                 album_info.artist)
-            item.albumartists = (album_info.artists_credit or
-                                 album_info.artists)
         else:
             item.artist = (track_info.artist or album_info.artist)
-            item.artists = (track_info.artists or album_info.artists)
             item.albumartist = album_info.artist
-            item.albumartists = album_info.artists
 
         # Album.
         item.album = album_info.album
 
         # Artist sort and credit names.
         item.artist_sort = track_info.artist_sort or album_info.artist_sort
-        item.artists_sort = track_info.artists_sort or album_info.artists_sort
         item.artist_credit = (track_info.artist_credit or
                               album_info.artist_credit)
-        item.artists_credit = (track_info.artists_credit or
-                               album_info.artists_credit)
         item.albumartist_sort = album_info.artist_sort
-        item.albumartists_sort = album_info.artists_sort
         item.albumartist_credit = album_info.artist_credit
-        item.albumartists_credit = album_info.artists_credit
 
         # Release date.
         for prefix in '', 'original_':
@@ -200,14 +174,7 @@ def apply_metadata(album_info: AlbumInfo, mapping: Mapping[Item, TrackInfo]):
             item.mb_artistid = track_info.artist_id
         else:
             item.mb_artistid = album_info.artist_id
-
-        if track_info.artists_ids:
-            item.mb_artistids = track_info.artists_ids
-        else:
-            item.mb_artistids = album_info.artists_ids
-
         item.mb_albumartistid = album_info.artist_id
-        item.mb_albumartistids = album_info.artists_ids
         item.mb_releasegroupid = album_info.releasegroup_id
 
         # Compilation flag.
diff --git a/beets/autotag/hooks.py b/beets/autotag/hooks.py
index 8d17e5729..96208dd0c 100644
--- a/beets/autotag/hooks.py
+++ b/beets/autotag/hooks.py
@@ -74,11 +74,8 @@ class AlbumInfo(AttrDict):
             album_id: Optional[str] = None,
             artist: Optional[str] = None,
             artist_id: Optional[str] = None,
-            artists: Optional[List[str]] = None,
-            artists_ids: Optional[List[str]] = None,
             asin: Optional[str] = None,
             albumtype: Optional[str] = None,
-            albumtypes: Optional[List[str]] = None,
             va: bool = False,
             year: Optional[int] = None,
             month: Optional[int] = None,
@@ -86,7 +83,6 @@ class AlbumInfo(AttrDict):
             label: Optional[str] = None,
             mediums: Optional[int] = None,
             artist_sort: Optional[str] = None,
-            artists_sort: Optional[List[str]] = None,
             releasegroup_id: Optional[str] = None,
             release_group_title: Optional[str] = None,
             catalognum: Optional[str] = None,
@@ -100,7 +96,6 @@ class AlbumInfo(AttrDict):
             albumdisambig: Optional[str] = None,
             releasegroupdisambig: Optional[str] = None,
             artist_credit: Optional[str] = None,
-            artists_credit: Optional[List[str]] = None,
             original_year: Optional[int] = None,
             original_month: Optional[int] = None,
             original_day: Optional[int] = None,
@@ -115,12 +110,9 @@ class AlbumInfo(AttrDict):
         self.album_id = album_id
         self.artist = artist
         self.artist_id = artist_id
-        self.artists = artists or []
-        self.artists_ids = artists_ids or []
         self.tracks = tracks
         self.asin = asin
         self.albumtype = albumtype
-        self.albumtypes = albumtypes or []
         self.va = va
         self.year = year
         self.month = month
@@ -128,7 +120,6 @@ class AlbumInfo(AttrDict):
         self.label = label
         self.mediums = mediums
         self.artist_sort = artist_sort
-        self.artists_sort = artists_sort or []
         self.releasegroup_id = releasegroup_id
         self.release_group_title = release_group_title
         self.catalognum = catalognum
@@ -142,7 +133,6 @@ class AlbumInfo(AttrDict):
         self.albumdisambig = albumdisambig
         self.releasegroupdisambig = releasegroupdisambig
         self.artist_credit = artist_credit
-        self.artists_credit = artists_credit or []
         self.original_year = original_year
         self.original_month = original_month
         self.original_day = original_day
@@ -200,18 +190,14 @@ class TrackInfo(AttrDict):
             release_track_id: Optional[str] = None,
             artist: Optional[str] = None,
             artist_id: Optional[str] = None,
-            artists: Optional[List[str]] = None,
-            artists_ids: Optional[List[str]] = None,
             length: Optional[float] = None,
             index: Optional[int] = None,
             medium: Optional[int] = None,
             medium_index: Optional[int] = None,
             medium_total: Optional[int] = None,
             artist_sort: Optional[str] = None,
-            artists_sort: Optional[List[str]] = None,
             disctitle: Optional[str] = None,
             artist_credit: Optional[str] = None,
-            artists_credit: Optional[List[str]] = None,
             data_source: Optional[str] = None,
             data_url: Optional[str] = None,
             media: Optional[str] = None,
@@ -234,8 +220,6 @@ class TrackInfo(AttrDict):
         self.release_track_id = release_track_id
         self.artist = artist
         self.artist_id = artist_id
-        self.artists = artists or []
-        self.artists_ids = artists_ids or []
         self.length = length
         self.index = index
         self.media = media
@@ -243,10 +227,8 @@ class TrackInfo(AttrDict):
         self.medium_index = medium_index
         self.medium_total = medium_total
         self.artist_sort = artist_sort
-        self.artists_sort = artists_sort or []
         self.disctitle = disctitle
         self.artist_credit = artist_credit
-        self.artists_credit = artists_credit or []
         self.data_source = data_source
         self.data_url = data_url
         self.lyricist = lyricist
diff --git a/beets/autotag/mb.py b/beets/autotag/mb.py
index 11a476e49..8fb8302e0 100644
--- a/beets/autotag/mb.py
+++ b/beets/autotag/mb.py
@@ -29,7 +29,6 @@ from beets import util
 from beets import config
 from collections import Counter
 from urllib.parse import urljoin
-
 from beets.util.id_extractors import extract_discogs_id_regex, \
     spotify_id_regex, deezer_id_regex, beatport_id_regex
 from beets.plugins import MetadataSourcePlugin
@@ -167,11 +166,9 @@ def _preferred_release_event(release: Dict[str, Any]) -> Tuple[str, str]:
     )
 
 
-def _multi_artist_credit(
-        credit: List[Dict], include_join_phrase: bool
-) -> Tuple[List[str], List[str], List[str]]:
-    """Given a list representing an ``artist-credit`` block, accumulate
-    data into a triple of joined artist name lists: canonical, sort, and
+def _flatten_artist_credit(credit: List[Dict]) -> Tuple[str, str, str]:
+    """Given a list representing an ``artist-credit`` block, flatten the
+    data into a triple of joined artist name strings: canonical, sort, and
     credit.
     """
     artist_parts = []
@@ -180,10 +177,9 @@ def _multi_artist_credit(
     for el in credit:
         if isinstance(el, str):
             # Join phrase.
-            if include_join_phrase:
-                artist_parts.append(el)
-                artist_credit_parts.append(el)
-                artist_sort_parts.append(el)
+            artist_parts.append(el)
+            artist_credit_parts.append(el)
+            artist_sort_parts.append(el)
 
         else:
             alias = _preferred_alias(el['artist'].get('alias-list', ()))
@@ -209,23 +205,6 @@ def _multi_artist_credit(
             else:
                 artist_credit_parts.append(cur_artist_name)
 
-    return (
-        artist_parts,
-        artist_sort_parts,
-        artist_credit_parts,
-    )
-
-
-def _flatten_artist_credit(credit: List[Dict]) -> Tuple[str, str, str]:
-    """Given a list representing an ``artist-credit`` block, flatten the
-    data into a triple of joined artist name strings: canonical, sort, and
-    credit.
-    """
-    artist_parts, artist_sort_parts, artist_credit_parts = \
-        _multi_artist_credit(
-            credit,
-            include_join_phrase=True
-        )
     return (
         ''.join(artist_parts),
         ''.join(artist_sort_parts),
@@ -233,19 +212,6 @@ def _flatten_artist_credit(credit: List[Dict]) -> Tuple[str, str, str]:
     )
 
 
-def _artist_ids(credit: List[Dict]) -> List[str]:
-    """
-    Given a list representing an ``artist-credit``,
-    return a list of artist IDs
-    """
-    artist_ids: List[str] = []
-    for el in credit:
-        if isinstance(el, dict):
-            artist_ids.append(el['artist']['id'])
-
-    return artist_ids
-
-
 def _get_related_artist_names(relations, relation_type):
     """Given a list representing the artist relationships extract the names of
     the remixers and concatenate them.
@@ -289,13 +255,9 @@ def track_info(
         info.artist, info.artist_sort, info.artist_credit = \
             _flatten_artist_credit(recording['artist-credit'])
 
-        info.artists, info.artists_sort, info.artists_credit = \
-            _multi_artist_credit(
-                recording['artist-credit'], include_join_phrase=False
-            )
-
-        info.artists_ids = _artist_ids(recording['artist-credit'])
-        info.artist_id = info.artists_ids[0]
+        # Get the ID and sort name of the first artist.
+        artist = recording['artist-credit'][0]['artist']
+        info.artist_id = artist['id']
 
     if recording.get('artist-relation-list'):
         info.remixer = _get_related_artist_names(
@@ -388,11 +350,6 @@ def album_info(release: Dict) -> beets.autotag.hooks.AlbumInfo:
     artist_name, artist_sort_name, artist_credit_name = \
         _flatten_artist_credit(release['artist-credit'])
 
-    artists_names, artists_sort_names, artists_credit_names = \
-        _multi_artist_credit(
-            release['artist-credit'], include_join_phrase=False
-        )
-
     ntracks = sum(len(m['track-list']) for m in release['medium-list'])
 
     # The MusicBrainz API omits 'artist-relation-list' and 'work-relation-list'
@@ -464,33 +421,21 @@ def album_info(release: Dict) -> beets.autotag.hooks.AlbumInfo:
                 # Get the artist names.
                 ti.artist, ti.artist_sort, ti.artist_credit = \
                     _flatten_artist_credit(track['artist-credit'])
-
-                ti.artists, ti.artists_sort, ti.artists_credit = \
-                    _multi_artist_credit(
-                        track['artist-credit'], include_join_phrase=False
-                    )
-
-                ti.artists_ids = _artist_ids(track['artist-credit'])
-                ti.artist_id = ti.artists_ids[0]
+                ti.artist_id = track['artist-credit'][0]['artist']['id']
             if track.get('length'):
                 ti.length = int(track['length']) / (1000.0)
 
             track_infos.append(ti)
 
-    album_artist_ids = _artist_ids(release['artist-credit'])
     info = beets.autotag.hooks.AlbumInfo(
         album=release['title'],
         album_id=release['id'],
         artist=artist_name,
-        artist_id=album_artist_ids[0],
-        artists=artists_names,
-        artists_ids=album_artist_ids,
+        artist_id=release['artist-credit'][0]['artist']['id'],
         tracks=track_infos,
         mediums=len(release['medium-list']),
         artist_sort=artist_sort_name,
-        artists_sort=artists_sort_names,
         artist_credit=artist_credit_name,
-        artists_credit=artists_credit_names,
         data_source='MusicBrainz',
         data_url=album_url(release['id']),
     )
diff --git a/beets/dbcore/types.py b/beets/dbcore/types.py
index 63829fcf6..7398731af 100644
--- a/beets/dbcore/types.py
+++ b/beets/dbcore/types.py
@@ -14,6 +14,7 @@
 
 """Representation of type information for DBCore model fields.
 """
+
 from abc import ABC
 import sys
 import typing
@@ -286,18 +287,18 @@ class DelimitedString(BaseString[List[str], List[str]]):
     """
     model_type = list
 
-    def __init__(self, delimiter: str):
+    def __init__(self, delimiter):
         self.delimiter = delimiter
 
-    def format(self, value: List[str]):
+    def format(self, value):
         return self.delimiter.join(value)
 
-    def parse(self, string: str):
+    def parse(self, string):
         if not string:
             return []
         return string.split(self.delimiter)
 
-    def to_sql(self, model_value: List[str]):
+    def to_sql(self, model_value):
         return self.delimiter.join(model_value)
 
 
@@ -325,6 +326,3 @@ NULL_FLOAT = NullFloat()
 STRING = String()
 BOOLEAN = Boolean()
 SEMICOLON_SPACE_DSV = DelimitedString(delimiter='; ')
-
-# Will set the proper null char in mediafile
-MULTI_VALUE_DSV = DelimitedString(delimiter='\\␀')
diff --git a/beets/importer.py b/beets/importer.py
index b00919404..67f111163 100644
--- a/beets/importer.py
+++ b/beets/importer.py
@@ -741,12 +741,8 @@ class ImportTask(BaseImportTask):
             # item.
             if not self.items[0].albumartist:
                 changes['albumartist'] = self.items[0].artist
-            if not self.items[0].albumartists:
-                changes['albumartists'] = self.items[0].artists
             if not self.items[0].mb_albumartistid:
                 changes['mb_albumartistid'] = self.items[0].mb_artistid
-            if not self.items[0].mb_albumartistids:
-                changes['mb_albumartistids'] = self.items[0].mb_artistids
 
         # Apply new metadata.
         for item in self.items:
diff --git a/beets/library.py b/beets/library.py
index ccd431b85..405b546e2 100644
--- a/beets/library.py
+++ b/beets/library.py
@@ -483,20 +483,13 @@ class Item(LibModel):
 
         'title': types.STRING,
         'artist': types.STRING,
-        'artists': types.MULTI_VALUE_DSV,
-        'artists_ids': types.MULTI_VALUE_DSV,
         'artist_sort': types.STRING,
-        'artists_sort': types.MULTI_VALUE_DSV,
         'artist_credit': types.STRING,
-        'artists_credit': types.MULTI_VALUE_DSV,
         'remixer': types.STRING,
         'album': types.STRING,
         'albumartist': types.STRING,
-        'albumartists': types.MULTI_VALUE_DSV,
         'albumartist_sort': types.STRING,
-        'albumartists_sort': types.MULTI_VALUE_DSV,
         'albumartist_credit': types.STRING,
-        'albumartists_credit': types.MULTI_VALUE_DSV,
         'genre': types.STRING,
         'style': types.STRING,
         'discogs_albumid': types.INTEGER,
@@ -524,9 +517,7 @@ class Item(LibModel):
         'mb_trackid': types.STRING,
         'mb_albumid': types.STRING,
         'mb_artistid': types.STRING,
-        'mb_artistids': types.MULTI_VALUE_DSV,
         'mb_albumartistid': types.STRING,
-        'mb_albumartistids': types.MULTI_VALUE_DSV,
         'mb_releasetrackid': types.STRING,
         'trackdisambig': types.STRING,
         'albumtype': types.STRING,
@@ -1079,9 +1070,6 @@ class Album(LibModel):
         'albumartist': types.STRING,
         'albumartist_sort': types.STRING,
         'albumartist_credit': types.STRING,
-        'albumartists': types.MULTI_VALUE_DSV,
-        'albumartists_sort': types.MULTI_VALUE_DSV,
-        'albumartists_credit': types.MULTI_VALUE_DSV,
         'album': types.STRING,
         'genre': types.STRING,
         'style': types.STRING,
@@ -1132,11 +1120,8 @@ class Album(LibModel):
     item_keys = [
         'added',
         'albumartist',
-        'albumartists',
         'albumartist_sort',
-        'albumartists_sort',
         'albumartist_credit',
-        'albumartists_credit',
         'album',
         'genre',
         'style',
diff --git a/beetsplug/scrub.py b/beetsplug/scrub.py
index d80446686..484ec073a 100644
--- a/beetsplug/scrub.py
+++ b/beetsplug/scrub.py
@@ -109,7 +109,7 @@ class ScrubPlugin(BeetsPlugin):
                 self._log.error('could not scrub {0}: {1}',
                                 util.displayable_path(path), exc)
 
-    def _scrub_item(self, item, restore=True):
+    def _scrub_item(self, item, restore):
         """Remove tags from an Item's associated file and, if `restore`
         is enabled, write the database's tags back to the file.
         """
@@ -146,4 +146,4 @@ class ScrubPlugin(BeetsPlugin):
         for item in task.imported_items():
             self._log.debug('auto-scrubbing {0}',
                             util.displayable_path(item.path))
-            self._scrub_item(item)
+            self._scrub_item(item, ui.should_write())
diff --git a/beetsplug/substitute.py b/beetsplug/substitute.py
deleted file mode 100644
index 120e62dfc..000000000
--- a/beetsplug/substitute.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# This file is part of beets.
-# Copyright 2023, Daniele Ferone.
-#
-# Permission is hereby granted, free of charge, to any person obtaining
-# a copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-
-"""The substitute plugin module.
-
-Uses user-specified substitution rules to canonicalize names for path formats.
-"""
-
-import re
-from beets.plugins import BeetsPlugin
-
-
-class Substitute(BeetsPlugin):
-    """The substitute plugin class.
-
-    Create a template field function that subsitute the given field with the
-    given substitution rules. ``rules`` must be a list of (pattern,
-    replacement) pairs.
-    """
-    def tmpl_substitute(self, text):
-        """Do the actual replacing."""
-        if text:
-            for pattern, replacement in self.substitute_rules:
-                if pattern.match(text.lower()):
-                    return replacement
-            return text
-        else:
-            return u''
-
-    def __init__(self):
-        """Initialize the substitute plugin.
-
-        Get the configuration, register template function and create list of
-        substitute rules.
-        """
-        super(Substitute, self).__init__()
-        self.substitute_rules = []
-        self.template_funcs['substitute'] = self.tmpl_substitute
-
-        for key, view in self.config.items():
-            value = view.as_str()
-            pattern = re.compile(key.lower())
-            self.substitute_rules.append((pattern, value))
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 82c0aea3d..5aa5ea0ec 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -19,7 +19,7 @@ New features:
   :bug:`4803`
 * Prevent reimporting album if it is permanently removed from Spotify
   :bug:`4800`
-* Added option to use `cover_art_url` as an album art source in the `fetchart` plugin.
+* Added option use `cover_art_arl` as an album art source in the `fetchart` plugin.
   :bug:`4707`
 * :doc:`/plugins/fetchart`: The plugin can now get album art from `spotify`.
 * Added option to specify a URL in the `embedart` plugin.
@@ -115,15 +115,13 @@ New features:
 * :doc:`/plugins/embyupdate`: Add handling for private users by adding
   ``userid`` config option.
   :bug:`4402`
-* :doc:`/plugins/substitute`: Add the new plugin `substitute` as an alternative
-  to the `rewrite` plugin. The main difference between them being that
-  `rewrite` modifies files' metadata and `substitute` does not.
-  :bug:`2786`
-* Add support for ``artists`` and ``albumartists`` multi-valued tags.
-  :bug:`505`
 
 Bug fixes:
 
+* :doc:`/plugins/scrub`: Fixed the import behavior where scrubbed database tags 
+  were restored to newly imported tracks with config settings ``scrub.auto: yes`` 
+  and ``import.write: no``.
+  :bug:`4326`
 * :doc:`/plugins/deezer`: Fixed the error where Deezer plugin would crash if non-Deezer id is passed during import.  
 * :doc:`/plugins/fetchart`: Fix fetching from Cover Art Archive when the
   `maxwidth` option is set to one of the supported Cover Art Archive widths.
diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst
index 21e99e3dc..96b0eb7a8 100644
--- a/docs/plugins/index.rst
+++ b/docs/plugins/index.rst
@@ -122,7 +122,6 @@ following to your configuration::
    spotify
    subsonicplaylist
    subsonicupdate
-   substitute
    the
    thumbnails
    types
@@ -241,11 +240,6 @@ Path Formats
 :doc:`rewrite <rewrite>`
    Substitute values in path formats.
 
-:doc:`substitute <substitute>`
-   As an alternative to :doc:`rewrite <rewrite>`, use this plugin. The main
-   difference between them is that this plugin never modifies the files
-   metadata.
-
 :doc:`the <the>`
    Move patterns in path formats (i.e., move "a" and "the" to the
    end).
diff --git a/docs/plugins/rewrite.rst b/docs/plugins/rewrite.rst
index 41cd454bf..3706d940a 100644
--- a/docs/plugins/rewrite.rst
+++ b/docs/plugins/rewrite.rst
@@ -30,9 +30,5 @@ As a convenience, the plugin applies patterns for the ``artist`` field to the
 ``albumartist`` field as well. (Otherwise, you would probably want to duplicate
 every rule for ``artist`` and ``albumartist``.)
 
-A word of warning: This plugin theoretically only applies to templates and path
-formats; it initially does not modify files' metadata tags or the values
-tracked by beets' library database, but since it *rewrites all field lookups*,
-it modifies the file's metadata anyway. See comments in issue :bug:`2786`.
-
-As an alternative to this plugin the :doc:`/plugins/substitute` could be used.
+Note that this plugin only applies to templating; it does not modify files'
+metadata tags or the values tracked by beets' library database.
diff --git a/docs/plugins/substitute.rst b/docs/plugins/substitute.rst
deleted file mode 100644
index 14f3cb025..000000000
--- a/docs/plugins/substitute.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-Substitute Plugin
-=================
-
-The ``substitute`` plugin lets you easily substitute values in your templates and
-path formats. Specifically, it is intended to let you *canonicalize* names
-such as artists: For example, perhaps you want albums from The Jimi Hendrix
-Experience to be sorted into the same folder as solo Hendrix albums.
-
-This plugin is intented as a replacement for the ``rewrite`` plugin. While
-the ``rewrite`` plugin modifies the metadata, this plugin does not.
-
-Enable the ``substitute`` plugin (see :ref:`using-plugins`), then make a ``substitute:`` section in your config file to contain your rules.
-Each rule consists of a case-insensitive regular expression pattern, and a
-replacement value. For example, you might use:
-
-    substitute:
-        .*jimi hendrix.*: Jimi Hendrix
-
-
-To apply the substitution, you have to call the function ``%substitute{}`` in the paths section. For example:
-    
-    paths:
-        default: %substitute{$albumartist}/$year - $album%aunique{}/$track - $title
\ No newline at end of file
diff --git a/docs/reference/query.rst b/docs/reference/query.rst
index 2bed2ed68..a3d7944bf 100644
--- a/docs/reference/query.rst
+++ b/docs/reference/query.rst
@@ -79,10 +79,6 @@ one that matches albums by year::
 Recall that ``-a`` makes the ``list`` command show albums instead of individual
 tracks, so this command shows me all the releases I have from this year.
 
-For multi-valued tags (such as ``artists`` or ``albumartists``), a regular
-expression search must be used to search for a single value within the
-multi-valued tag.
-
 Phrases
 -------
 
diff --git a/setup.py b/setup.py
index 26eb8048a..729d5003f 100755
--- a/setup.py
+++ b/setup.py
@@ -88,7 +88,7 @@ setup(
         'unidecode>=1.3.6',
         'musicbrainzngs>=0.4',
         'pyyaml',
-        'mediafile>=0.12.0',
+        'mediafile>=0.9.0',
         'confuse>=1.5.0',
         'munkres>=1.0.0',
         'jellyfish',


Test patch:
diff --git a/test/test_autotag.py b/test/test_autotag.py
index ae607cb19..2314b42e0 100644
--- a/test/test_autotag.py
+++ b/test/test_autotag.py
@@ -628,9 +628,7 @@ class ApplyTest(_common.TestCase, ApplyTestUtil):
             medium_total=1,
             index=1,
             artist_credit='trackArtistCredit',
-            artists_credit=['trackArtistCredit'],
             artist_sort='trackArtistSort',
-            artists_sort=['trackArtistSort'],
         ))
         trackinfo.append(TrackInfo(
             title='twoNew',
@@ -643,18 +641,11 @@ class ApplyTest(_common.TestCase, ApplyTestUtil):
         self.info = AlbumInfo(
             tracks=trackinfo,
             artist='artistNew',
-            artists=['artistNew', 'artistNew2'],
             album='albumNew',
             album_id='7edb51cb-77d6-4416-a23c-3a8c2994a2c7',
             artist_id='a6623d39-2d8e-4f70-8242-0a9553b91e50',
-            artists_ids=[
-                'a6623d39-2d8e-4f70-8242-0a9553b91e50',
-                'a6623d39-2d8e-4f70-8242-0a9553b91e51'
-            ],
             artist_credit='albumArtistCredit',
-            artists_credit=['albumArtistCredit', 'albumArtistCredit2'],
             artist_sort='albumArtistSort',
-            artists_sort=['albumArtistSort', 'albumArtistSort2'],
             albumtype='album',
             va=False,
             mediums=2,
@@ -671,14 +662,6 @@ class ApplyTest(_common.TestCase, ApplyTestUtil):
         self.assertEqual(self.items[1].album, 'albumNew')
         self.assertEqual(self.items[0].artist, 'artistNew')
         self.assertEqual(self.items[1].artist, 'artistNew')
-        self.assertEqual(self.items[0].artists, ['artistNew', 'artistNew2'])
-        self.assertEqual(self.items[1].artists, ['artistNew', 'artistNew2'])
-        self.assertEqual(
-            self.items[0].albumartists, ['artistNew', 'artistNew2']
-        )
-        self.assertEqual(
-            self.items[1].albumartists, ['artistNew', 'artistNew2']
-        )
 
     def test_track_index_applied(self):
         self._apply()
@@ -716,14 +699,6 @@ class ApplyTest(_common.TestCase, ApplyTestUtil):
         self.assertEqual(self.items[1].artist, 'albumArtistCredit')
         self.assertEqual(self.items[0].albumartist, 'albumArtistCredit')
         self.assertEqual(self.items[1].albumartist, 'albumArtistCredit')
-        self.assertEqual(
-            self.items[0].albumartists,
-            ['albumArtistCredit', 'albumArtistCredit2']
-        )
-        self.assertEqual(
-            self.items[1].albumartists,
-            ['albumArtistCredit', 'albumArtistCredit2']
-        )
 
     def test_artist_credit_prefers_artist_over_albumartist_credit(self):
         self.info.tracks[0].artist = 'oldArtist'
@@ -750,13 +725,6 @@ class ApplyTest(_common.TestCase, ApplyTestUtil):
                              '7edb51cb-77d6-4416-a23c-3a8c2994a2c7')
             self.assertEqual(item.mb_artistid,
                              'a6623d39-2d8e-4f70-8242-0a9553b91e50')
-            self.assertEqual(
-                item.mb_artistids,
-                [
-                    'a6623d39-2d8e-4f70-8242-0a9553b91e50',
-                    'a6623d39-2d8e-4f70-8242-0a9553b91e51',
-                ]
-            )
 
     def test_albumtype_applied(self):
         self._apply()
@@ -768,60 +736,28 @@ class ApplyTest(_common.TestCase, ApplyTestUtil):
         self._apply(info=my_info)
         self.assertEqual(self.items[0].artist, 'artistNew')
         self.assertEqual(self.items[1].artist, 'artistNew')
-        self.assertEqual(self.items[0].artists, ['artistNew', 'artistNew2'])
-        self.assertEqual(self.items[1].artists, ['artistNew', 'artistNew2'])
 
     def test_album_artist_overridden_by_nonempty_track_artist(self):
         my_info = self.info.copy()
         my_info.tracks[0].artist = 'artist1!'
         my_info.tracks[1].artist = 'artist2!'
-        my_info.tracks[0].artists = ['artist1!', 'artist1!!']
-        my_info.tracks[1].artists = ['artist2!', 'artist2!!']
         self._apply(info=my_info)
         self.assertEqual(self.items[0].artist, 'artist1!')
         self.assertEqual(self.items[1].artist, 'artist2!')
-        self.assertEqual(self.items[0].artists, ['artist1!', 'artist1!!'])
-        self.assertEqual(self.items[1].artists, ['artist2!', 'artist2!!'])
 
     def test_artist_credit_applied(self):
         self._apply()
         self.assertEqual(self.items[0].albumartist_credit, 'albumArtistCredit')
-        self.assertEqual(
-            self.items[0].albumartists_credit,
-            ['albumArtistCredit', 'albumArtistCredit2']
-        )
         self.assertEqual(self.items[0].artist_credit, 'trackArtistCredit')
-        self.assertEqual(self.items[0].artists_credit, ['trackArtistCredit'])
         self.assertEqual(self.items[1].albumartist_credit, 'albumArtistCredit')
-        self.assertEqual(
-            self.items[1].albumartists_credit,
-            ['albumArtistCredit', 'albumArtistCredit2']
-        )
         self.assertEqual(self.items[1].artist_credit, 'albumArtistCredit')
-        self.assertEqual(
-            self.items[1].artists_credit,
-            ['albumArtistCredit', 'albumArtistCredit2']
-        )
 
     def test_artist_sort_applied(self):
         self._apply()
         self.assertEqual(self.items[0].albumartist_sort, 'albumArtistSort')
-        self.assertEqual(
-            self.items[0].albumartists_sort,
-            ['albumArtistSort', 'albumArtistSort2']
-        )
         self.assertEqual(self.items[0].artist_sort, 'trackArtistSort')
-        self.assertEqual(self.items[0].artists_sort, ['trackArtistSort'])
         self.assertEqual(self.items[1].albumartist_sort, 'albumArtistSort')
-        self.assertEqual(
-            self.items[1].albumartists_sort,
-            ['albumArtistSort', 'albumArtistSort2']
-        )
         self.assertEqual(self.items[1].artist_sort, 'albumArtistSort')
-        self.assertEqual(
-            self.items[1].artists_sort,
-            ['albumArtistSort', 'albumArtistSort2']
-        )
 
     def test_full_date_applied(self):
         my_info = self.info.copy()
diff --git a/test/test_edit.py b/test/test_edit.py
index ad43ca839..3926b2ebc 100644
--- a/test/test_edit.py
+++ b/test/test_edit.py
@@ -312,9 +312,7 @@ class EditDuringImporterTest(TerminalImportSessionSetup, unittest.TestCase,
         self.assertItemFieldsModified(self.lib.items(), self.items_orig,
                                       ['title'],
                                       self.IGNORED + ['albumartist',
-                                                      'mb_albumartistid',
-                                                      'mb_albumartistids',
-                                                      ])
+                                                      'mb_albumartistid'])
         self.assertTrue(all('Edited Title' in i.title
                             for i in self.lib.items()))
 
diff --git a/test/test_importer.py b/test/test_importer.py
index 233f9bc90..a948a5efd 100644
--- a/test/test_importer.py
+++ b/test/test_importer.py
@@ -244,6 +244,60 @@ class ImportHelper(TestHelper):
         self.assertEqual(len(os.listdir(syspath(self.libdir))), 0)
 
 
+class ScrubbedImportTest(_common.TestCase, ImportHelper):
+    def setUp(self):
+        self.setup_beets(disk=True)
+        self.load_plugins('scrub')
+        self._create_import_dir(2)
+        self._setup_import_session(autotag=False)
+
+    def tearDown(self):
+        self.unload_plugins()
+        self.teardown_beets()
+
+    def test_tags_not_scrubbed(self):
+        config['plugins'] = ['scrub']
+        config['scrub']['auto'] = False
+        config['import']['write'] = True
+        for mediafile in self.import_media:
+            self.assertEqual(mediafile.artist, 'Tag Artist')
+            self.assertEqual(mediafile.album, 'Tag Album')
+        self.importer.run()
+        for item in self.lib.items():
+            imported_file = os.path.join(item.path)
+            imported_file = MediaFile(imported_file)
+            self.assertEqual(imported_file.artist, 'Tag Artist')
+            self.assertEqual(imported_file.album, 'Tag Album')
+
+    def test_tags_restored(self):
+        config['plugins'] = ['scrub']
+        config['scrub']['auto'] = True
+        config['import']['write'] = True
+        for mediafile in self.import_media:
+            self.assertEqual(mediafile.artist, 'Tag Artist')
+            self.assertEqual(mediafile.album, 'Tag Album')
+        self.importer.run()
+        for item in self.lib.items():
+            imported_file = os.path.join(item.path)
+            imported_file = MediaFile(imported_file)
+            self.assertEqual(imported_file.artist, 'Tag Artist')
+            self.assertEqual(imported_file.album, 'Tag Album')
+
+    def test_tags_not_restored(self):
+        config['plugins'] = ['scrub']
+        config['scrub']['auto'] = True
+        config['import']['write'] = False
+        for mediafile in self.import_media:
+            self.assertEqual(mediafile.artist, 'Tag Artist')
+            self.assertEqual(mediafile.album, 'Tag Album')
+        self.importer.run()
+        for item in self.lib.items():
+            imported_file = os.path.join(item.path)
+            imported_file = MediaFile(imported_file)
+            self.assertEqual(imported_file.artist, None)
+            self.assertEqual(imported_file.album, None)
+
+
 @_common.slow_test()
 class NonAutotaggedImportTest(_common.TestCase, ImportHelper):
     def setUp(self):
@@ -868,7 +922,7 @@ class ImportCompilationTest(_common.TestCase, ImportHelper):
         for item in self.lib.items():
             self.assertEqual(item.albumartist, 'Various Artists')
 
-    def test_asis_heterogenous_sets_compilation(self):
+    def test_asis_heterogenous_sets_sompilation(self):
         self.import_media[0].artist = 'Other Artist'
         self.import_media[0].save()
         self.import_media[1].artist = 'Another Artist'
@@ -908,53 +962,6 @@ class ImportCompilationTest(_common.TestCase, ImportHelper):
             self.assertEqual(item.albumartist, 'Album Artist')
             self.assertEqual(item.mb_albumartistid, 'Album Artist ID')
 
-    def test_asis_albumartists_tag_sets_multi_albumartists(self):
-        self.import_media[0].artist = 'Other Artist'
-        self.import_media[0].artists = ['Other Artist', 'Other Artist 2']
-        self.import_media[1].artist = 'Another Artist'
-        self.import_media[1].artists = ['Another Artist', 'Another Artist 2']
-        for mediafile in self.import_media:
-            mediafile.albumartist = 'Album Artist'
-            mediafile.albumartists = ['Album Artist 1', 'Album Artist 2']
-            mediafile.mb_albumartistid = 'Album Artist ID'
-            mediafile.save()
-
-        self.importer.add_choice(importer.action.ASIS)
-        self.importer.run()
-        self.assertEqual(self.lib.albums().get().albumartist, 'Album Artist')
-        self.assertEqual(
-            self.lib.albums().get().albumartists,
-            ['Album Artist 1', 'Album Artist 2']
-        )
-        self.assertEqual(self.lib.albums().get().mb_albumartistid,
-                         'Album Artist ID')
-
-        # Make sure both custom media items get tested
-        asserted_multi_artists_0 = False
-        asserted_multi_artists_1 = False
-        for item in self.lib.items():
-            self.assertEqual(item.albumartist, 'Album Artist')
-            self.assertEqual(
-                item.albumartists,
-                ['Album Artist 1', 'Album Artist 2']
-            )
-            self.assertEqual(item.mb_albumartistid, 'Album Artist ID')
-
-            if item.artist == "Other Artist":
-                asserted_multi_artists_0 = True
-                self.assertEqual(
-                    item.artists,
-                    ['Other Artist', 'Other Artist 2']
-                )
-            if item.artist == "Another Artist":
-                asserted_multi_artists_1 = True
-                self.assertEqual(
-                    item.artists,
-                    ['Another Artist', 'Another Artist 2']
-                )
-
-        self.assertTrue(asserted_multi_artists_0 and asserted_multi_artists_1)
-
 
 class ImportExistingTest(_common.TestCase, ImportHelper):
     """Test importing files that are already in the library directory.
diff --git a/test/test_info.py b/test/test_info.py
index 929a83d80..94923a37f 100644
--- a/test/test_info.py
+++ b/test/test_info.py
@@ -90,34 +90,6 @@ class InfoTest(unittest.TestCase, TestHelper):
         self.assertIn('title: [various]', out)
         self.remove_mediafile_fixtures()
 
-    def test_collect_item_and_path_with_multi_values(self):
-        path = self.create_mediafile_fixture()
-        mediafile = MediaFile(path)
-        item, = self.add_item_fixtures()
-
-        item.album = mediafile.album = 'AAA'
-        item.tracktotal = mediafile.tracktotal = 5
-        item.title = 'TTT'
-        mediafile.title = 'SSS'
-
-        item.albumartists = ['Artist A', 'Artist B']
-        mediafile.albumartists = ['Artist C', 'Artist D']
-
-        item.artists = ['Artist A', 'Artist Z']
-        mediafile.artists = ['Artist A', 'Artist Z']
-
-        item.write()
-        item.store()
-        mediafile.save()
-
-        out = self.run_with_output('info', '--summarize', 'album:AAA', path)
-        self.assertIn('album: AAA', out)
-        self.assertIn('tracktotal: 5', out)
-        self.assertIn('title: [various]', out)
-        self.assertIn('albumartists: [various]', out)
-        self.assertIn('artists: Artist A; Artist Z', out)
-        self.remove_mediafile_fixtures()
-
     def test_custom_format(self):
         self.add_item_fixtures()
         out = self.run_with_output('info', '--library', '--format',
diff --git a/test/test_library.py b/test/test_library.py
index 651f84678..269771575 100644
--- a/test/test_library.py
+++ b/test/test_library.py
@@ -1312,27 +1312,6 @@ class WriteTest(unittest.TestCase, TestHelper):
         self.assertNotEqual(item.artist, 'new artist')
         self.assertEqual(MediaFile(syspath(item.path)).artist, 'new artist')
 
-    def test_write_multi_tags(self):
-        item = self.add_item_fixture(artist='old artist')
-        item.write(tags={'artists': ['old artist', 'another artist']})
-
-        self.assertEqual(
-            MediaFile(syspath(item.path)).artists,
-            ['old artist', 'another artist']
-        )
-
-    def test_write_multi_tags_id3v23(self):
-        item = self.add_item_fixture(artist='old artist')
-        item.write(
-            tags={'artists': ['old artist', 'another artist']},
-            id3v23=True
-        )
-
-        self.assertEqual(
-            MediaFile(syspath(item.path)).artists,
-            ['old artist/another artist']
-        )
-
     def test_write_date_field(self):
         # Since `date` is not a MediaField, this should do nothing.
         item = self.add_item_fixture()
diff --git a/test/test_mb.py b/test/test_mb.py
index 9e6ac906a..03a1912ed 100644
--- a/test/test_mb.py
+++ b/test/test_mb.py
@@ -24,16 +24,9 @@ from unittest import mock
 
 
 class MBAlbumInfoTest(_common.TestCase):
-    def _make_release(
-        self,
-        date_str='2009',
-        tracks=None,
-        track_length=None,
-        track_artist=False,
-        multi_artist_credit=False,
-        data_tracks=None,
-        medium_format='FORMAT'
-    ):
+    def _make_release(self, date_str='2009', tracks=None, track_length=None,
+                      track_artist=False, data_tracks=None,
+                      medium_format='FORMAT'):
         release = {
             'title': 'ALBUM TITLE',
             'id': 'ALBUM ID',
@@ -68,18 +61,6 @@ class MBAlbumInfoTest(_common.TestCase):
             'country': 'COUNTRY',
             'status': 'STATUS',
         }
-
-        if multi_artist_credit:
-            release['artist-credit'].append(" & ")  # add join phase
-            release['artist-credit'].append({
-                'artist': {
-                    'name': 'ARTIST 2 NAME',
-                    'id': 'ARTIST 2 ID',
-                    'sort-name': 'ARTIST 2 SORT NAME',
-                },
-                'name': 'ARTIST MULTI CREDIT',
-            })
-
         i = 0
         track_list = []
         if tracks:
@@ -107,20 +88,6 @@ class MBAlbumInfoTest(_common.TestCase):
                             'name': 'TRACK ARTIST CREDIT',
                         }
                     ]
-
-                    if multi_artist_credit:
-                        track['artist-credit'].append(" & ")  # add join phase
-                        track['artist-credit'].append(
-                            {
-                                'artist': {
-                                    'name': 'TRACK ARTIST 2 NAME',
-                                    'id': 'TRACK ARTIST 2 ID',
-                                    'sort-name': 'TRACK ARTIST 2 SORT NAME',
-                                },
-                                'name': 'TRACK ARTIST 2 CREDIT',
-                            }
-                        )
-
                 track_list.append(track)
         data_track_list = []
         if data_tracks:
@@ -142,17 +109,8 @@ class MBAlbumInfoTest(_common.TestCase):
         })
         return release
 
-    def _make_track(
-        self,
-        title,
-        tr_id,
-        duration,
-        artist=False,
-        video=False,
-        disambiguation=None,
-        remixer=False,
-        multi_artist_credit=False
-    ):
+    def _make_track(self, title, tr_id, duration, artist=False,
+                    video=False, disambiguation=None, remixer=False):
         track = {
             'title': title,
             'id': tr_id,
@@ -170,18 +128,6 @@ class MBAlbumInfoTest(_common.TestCase):
                     'name': 'RECORDING ARTIST CREDIT',
                 }
             ]
-            if multi_artist_credit:
-                track['artist-credit'].append(" & ")  # add join phase
-                track['artist-credit'].append(
-                    {
-                        'artist': {
-                            'name': 'RECORDING ARTIST 2 NAME',
-                            'id': 'RECORDING ARTIST 2 ID',
-                            'sort-name': 'RECORDING ARTIST 2 SORT NAME',
-                        },
-                        'name': 'RECORDING ARTIST 2 CREDIT',
-                    }
-                )
         if remixer:
             track['artist-relation-list'] = [
                 {
@@ -400,52 +346,6 @@ class MBAlbumInfoTest(_common.TestCase):
         self.assertEqual(track.artist_sort, 'RECORDING ARTIST SORT NAME')
         self.assertEqual(track.artist_credit, 'RECORDING ARTIST CREDIT')
 
-    def test_parse_recording_artist_multi(self):
-        tracks = [
-            self._make_track(
-                'a', 'b', 1, True, multi_artist_credit=True
-            )
-        ]
-        release = self._make_release(None, tracks=tracks)
-        track = mb.album_info(release).tracks[0]
-        self.assertEqual(
-            track.artist,
-            'RECORDING ARTIST NAME & RECORDING ARTIST 2 NAME'
-        )
-        self.assertEqual(
-            track.artist_id, 'RECORDING ARTIST ID'
-        )
-        self.assertEqual(
-            track.artist_sort,
-            'RECORDING ARTIST SORT NAME & RECORDING ARTIST 2 SORT NAME'
-        )
-        self.assertEqual(
-            track.artist_credit,
-            'RECORDING ARTIST CREDIT & RECORDING ARTIST 2 CREDIT'
-        )
-
-        self.assertEqual(
-            track.artists,
-            ['RECORDING ARTIST NAME', 'RECORDING ARTIST 2 NAME']
-        )
-        self.assertEqual(
-            track.artists_ids, ['RECORDING ARTIST ID', 'RECORDING ARTIST 2 ID']
-        )
-        self.assertEqual(
-            track.artists_sort,
-            [
-                'RECORDING ARTIST SORT NAME',
-                'RECORDING ARTIST 2 SORT NAME'
-            ]
-        )
-        self.assertEqual(
-            track.artists_credit,
-            [
-                'RECORDING ARTIST CREDIT',
-                'RECORDING ARTIST 2 CREDIT'
-            ]
-        )
-
     def test_track_artist_overrides_recording_artist(self):
         tracks = [self._make_track('a', 'b', 1, True)]
         release = self._make_release(None, tracks=tracks, track_artist=True)
@@ -455,48 +355,6 @@ class MBAlbumInfoTest(_common.TestCase):
         self.assertEqual(track.artist_sort, 'TRACK ARTIST SORT NAME')
         self.assertEqual(track.artist_credit, 'TRACK ARTIST CREDIT')
 
-    def test_track_artist_overrides_recording_artist_multi(self):
-        tracks = [
-            self._make_track('a', 'b', 1, True, multi_artist_credit=True)
-        ]
-        release = self._make_release(
-            None,
-            tracks=tracks,
-            track_artist=True,
-            multi_artist_credit=True
-        )
-        track = mb.album_info(release).tracks[0]
-        self.assertEqual(
-            track.artist,
-            'TRACK ARTIST NAME & TRACK ARTIST 2 NAME'
-        )
-        self.assertEqual(track.artist_id, 'TRACK ARTIST ID')
-        self.assertEqual(
-            track.artist_sort,
-            'TRACK ARTIST SORT NAME & TRACK ARTIST 2 SORT NAME'
-        )
-        self.assertEqual(
-            track.artist_credit,
-            'TRACK ARTIST CREDIT & TRACK ARTIST 2 CREDIT'
-        )
-
-        self.assertEqual(
-            track.artists,
-            ['TRACK ARTIST NAME', 'TRACK ARTIST 2 NAME']
-        )
-        self.assertEqual(
-            track.artists_ids,
-            ['TRACK ARTIST ID', 'TRACK ARTIST 2 ID']
-        )
-        self.assertEqual(
-            track.artists_sort,
-            ['TRACK ARTIST SORT NAME', 'TRACK ARTIST 2 SORT NAME']
-        )
-        self.assertEqual(
-            track.artists_credit,
-            ['TRACK ARTIST CREDIT', 'TRACK ARTIST 2 CREDIT']
-        )
-
     def test_parse_recording_remixer(self):
         tracks = [self._make_track('a', 'b', 1, remixer=True)]
         release = self._make_release(None, tracks=tracks)
@@ -664,31 +522,19 @@ class ArtistFlatteningTest(_common.TestCase):
         credit_dict['artist']['alias-list'].append(alias)
 
     def test_single_artist(self):
-        credit = [self._credit_dict()]
-        a, s, c = mb._flatten_artist_credit(credit)
+        a, s, c = mb._flatten_artist_credit([self._credit_dict()])
         self.assertEqual(a, 'NAME')
         self.assertEqual(s, 'SORT')
         self.assertEqual(c, 'CREDIT')
 
-        a, s, c = mb._multi_artist_credit(credit, include_join_phrase=False)
-        self.assertEqual(a, ['NAME'])
-        self.assertEqual(s, ['SORT'])
-        self.assertEqual(c, ['CREDIT'])
-
     def test_two_artists(self):
-        credit = [self._credit_dict('a'), ' AND ', self._credit_dict('b')]
         a, s, c = mb._flatten_artist_credit(
-            credit
+            [self._credit_dict('a'), ' AND ', self._credit_dict('b')]
         )
         self.assertEqual(a, 'NAMEa AND NAMEb')
         self.assertEqual(s, 'SORTa AND SORTb')
         self.assertEqual(c, 'CREDITa AND CREDITb')
 
-        a, s, c = mb._multi_artist_credit(credit, include_join_phrase=False)
-        self.assertEqual(a, ['NAMEa', 'NAMEb'])
-        self.assertEqual(s, ['SORTa', 'SORTb'])
-        self.assertEqual(c, ['CREDITa', 'CREDITb'])
-
     def test_alias(self):
         credit_dict = self._credit_dict()
         self._add_alias(credit_dict, suffix='en', locale='en', primary=True)
diff --git a/test/test_plugin_mediafield.py b/test/test_plugin_mediafield.py
index 475d3a377..23899e894 100644
--- a/test/test_plugin_mediafield.py
+++ b/test/test_plugin_mediafield.py
@@ -33,13 +33,6 @@ field_extension = mediafile.MediaField(
     mediafile.ASFStorageStyle('customtag'),
 )
 
-list_field_extension = mediafile.ListMediaField(
-    mediafile.MP3ListDescStorageStyle('customlisttag'),
-    mediafile.MP4ListStorageStyle('----:com.apple.iTunes:customlisttag'),
-    mediafile.ListStorageStyle('customlisttag'),
-    mediafile.ASFStorageStyle('customlisttag'),
-)
-
 
 class ExtendedFieldTestMixin(_common.TestCase):
 
@@ -66,22 +59,6 @@ class ExtendedFieldTestMixin(_common.TestCase):
             delattr(mediafile.MediaFile, 'customtag')
             Item._media_fields.remove('customtag')
 
-    def test_extended_list_field_write(self):
-        plugin = BeetsPlugin()
-        plugin.add_media_field('customlisttag', list_field_extension)
-
-        try:
-            mf = self._mediafile_fixture('empty')
-            mf.customlisttag = ['a', 'b']
-            mf.save()
-
-            mf = mediafile.MediaFile(mf.path)
-            self.assertEqual(mf.customlisttag, ['a', 'b'])
-
-        finally:
-            delattr(mediafile.MediaFile, 'customlisttag')
-            Item._media_fields.remove('customlisttag')
-
     def test_write_extended_tag_from_item(self):
         plugin = BeetsPlugin()
         plugin.add_media_field('customtag', field_extension)
diff --git a/test/test_query.py b/test/test_query.py
index 5f8450c49..4e0a748cb 100644
--- a/test/test_query.py
+++ b/test/test_query.py
@@ -95,21 +95,18 @@ class DummyDataTestCase(_common.TestCase, AssertsMixin):
         items = [_common.item() for _ in range(3)]
         items[0].title = 'foo bar'
         items[0].artist = 'one'
-        items[0].artists = ['one', 'eleven']
         items[0].album = 'baz'
         items[0].year = 2001
         items[0].comp = True
         items[0].genre = 'rock'
         items[1].title = 'baz qux'
         items[1].artist = 'two'
-        items[1].artists = ['two', 'twelve']
         items[1].album = 'baz'
         items[1].year = 2002
         items[1].comp = True
         items[1].genre = 'Rock'
         items[2].title = 'beets 4 eva'
         items[2].artist = 'three'
-        items[2].artists = ['three', 'one']
         items[2].album = 'foo'
         items[2].year = 2003
         items[2].comp = False
@@ -218,16 +215,6 @@ class GetTest(DummyDataTestCase):
         results = self.lib.items(q)
         self.assert_items_matched(results, ['beets 4 eva'])
 
-    def test_term_case_regex_with_multi_key_matches(self):
-        q = 'artists::eleven'
-        results = self.lib.items(q)
-        self.assert_items_matched(results, ['foo bar'])
-
-    def test_term_case_regex_with_multi_key_matches_multiple_columns(self):
-        q = 'artists::one'
-        results = self.lib.items(q)
-        self.assert_items_matched(results, ['foo bar', 'beets 4 eva'])
-
     def test_key_case_insensitive(self):
         q = 'ArTiST:three'
         results = self.lib.items(q)