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:
Thank you for the detailed report! It does seem like this is a (fairly obvious in retrospect!) bug. Since the documentation says one thing and the plugin does the other, I think we should follow the docs. (It seems like the plugin is missing any sort of check whatsoever.)
for reference:

https://github.com/beetbox/beets/blob/e977a6f799f7f166b20bee7d41f285b989b5d899/beetsplug/scrub.py#L144

Resolving patch:
diff --git a/beetsplug/scrub.py b/beetsplug/scrub.py
--- a/beetsplug/scrub.py
+++ b/beetsplug/scrub.py
@@ -109,7 +109,7 @@ def _scrub(self, path):
                 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 @@ def import_task_files(self, session, task):
         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())


Test patch:
diff --git a/test/test_importer.py b/test/test_importer.py
--- a/test/test_importer.py
+++ b/test/test_importer.py
@@ -244,6 +244,60 @@ def assert_lib_dir_empty(self):
         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):