gtronset/beets-filetote

unknown move operation while moving into library

Closed this issue · 7 comments

I think you're already aware of this given the TODO in the code, but thought I'd track it anyways

while trying to import an album on:

beets version 1.6.0
Python version 3.10.9
plugins: chroma, convert, discogs, edit, export, fetchart, filetote, ftintitle, info, lastgenre, scrub, zero

my beet config is here and I use this script to import my music from a downloads folder, in particular it calls like:

beet import --move --noincremental --from-scratch ~/Downloads/...

$ ls -1
'01 - Close to your Mind.mp3'
'02 - darling, would you catch me.mp3'
'03 - Acoustic Image.mp3'
'04 - アリスのボサノバ.mp3'
'05 - relative relation (it'\''s all about).mp3'
'06 - Etupirka, Angelica.mp3'
'07 - 白玉茶屋in冥土.mp3'
'08 - ナルキッソスにさよなら.mp3'
'09 - タイニーリトル・アジアンタム.mp3'
'10 - Full Moon Samba.mp3'
'11 - なんてことない日.mp3'
folder.jpg

just added a breakpoint so I could see what may be happening:

diff --git a/beetsplug/filetote.py b/beetsplug/filetote.py
index abd2b17..d010949 100644
--- a/beetsplug/filetote.py
+++ b/beetsplug/filetote.py
@@ -329,6 +329,7 @@ class FiletotePlugin(BeetsPlugin):
                 ignored_files.append(source_file)
                 continue
 
+            breakpoint()
             dest_file = util.unique_path(dest_file)
             util.mkdirall(dest_file)
             dest_file = util.bytestring_path(dest_file)
> /home/sean/.local/lib/python3.10/site-packages/beetsplug/filetote.py(334)process_artifacts()
    333             dest_file = util.unique_path(dest_file)
--> 334             util.mkdirall(dest_file)
    335             dest_file = util.bytestring_path(dest_file)

ipdb> dest_file
'/home/sean/Music/Shibayan Records/TOHO BOSSA NOVA 2/folder.jpg'
> /home/sean/.local/lib/python3.10/site-packages/beetsplug/filetote.py(340)process_artifacts()
    339 
--> 340             self.manipulate_artifact(source_file, dest_file)
    341 

ipdb> n
AssertionError: unknown MoveOperation None

does detect as move properly I think?

ipdb> self._operation_type()
<MoveOperation.MOVE: 0>
ipdb>

just not sure why its None if _operation_type returns MoveOperation.MOVE

Nevermind, it may not detect it as move, that might have been the next loop iteration for the files, added a basic log and it shows up as None:

diff --git a/beetsplug/filetote.py b/beetsplug/filetote.py
index abd2b17..9095553 100644
--- a/beetsplug/filetote.py
+++ b/beetsplug/filetote.py
@@ -27,6 +27,7 @@ class FiletotePlugin(BeetsPlugin):
         )
 
         self.operation = self._operation_type()
+        self._log.info(f"operation type {self.operation}")
 
         self._process_queue = []
         self._shared_artifacts = {}

output: https://gist.github.com/seanbreckenridge/a7962a6e68b7eccfc870e16f1a1fac4d

My point for using the plugin is to copy over jpg/pngs to the when moving from my downloads folder to my music folder, so this may have just been user error, since I do see at the bottom:

filetote: Ignored files:
filetote:    cover.jpg

The cover.jpg file does end up in the target directory, but am not sure if thats filetote doing that, or fetchart. will disable fetchart next time I import a library and see if that changes

Config Im using:

paths:
  filename:cover.jpg: $albumpath/cover

# plugin config
filetote:
  extensions: .png .jpg
  filename: "cover.jpg"
  print_ignored: yes

will see if the error continues to happen and print an error when/if it happens again

Had another thought (may be wrong) -- I'm using the --move flag and move is not set in my config. Is the plugin loaded before the cli flags are parsed? Since in the log gist above it shows up as None but when I run the function later in the execution manually through pdb it's set to Move.

This is the typical error I receive when trying to do:

beet import --move --noincremental --from-scratch ./Folder:

If theres a cover.jpg in the folder:

Sending event: after_write
Sending event: album_imported
Sending event: import
Sending event: cli_exit
filetote: None-ing artifact: cover.jpg
Traceback (most recent call last):
  File "/home/sean/.local/bin/beet", line 8, in <module>
    sys.exit(main())
  File "/home/sean/.local/lib/python3.10/site-packages/beets/ui/__init__.py", line 1285, in main
    _raw_main(args)
  File "/home/sean/.local/lib/python3.10/site-packages/beets/ui/__init__.py", line 1274, in _ra                         w_main
    plugins.send('cli_exit', lib=lib)
  File "/home/sean/.local/lib/python3.10/site-packages/beets/plugins.py", line 488, in send
    result = handler(**arguments)
  File "/home/sean/.local/lib/python3.10/site-packages/beets/plugins.py", line 145, in wrapper
    return func(*args, **kwargs)
  File "/home/sean/.local/lib/python3.10/site-packages/beetsplug/filetote.py", line 286, in process_events
    self.process_artifacts(artifacts, item["mapping"])
  File "/home/sean/.local/lib/python3.10/site-packages/beetsplug/filetote.py", line 340, in process_artifacts
    self.manipulate_artifact(source_file, dest_file)
  File "/home/sean/.local/lib/python3.10/site-packages/beetsplug/filetote.py", line 403, in manipulate_artifact
    assert False, f"unknown MoveOperation {self.operation}"
AssertionError: unknown MoveOperation None

verbose log: https://gist.github.com/seanbreckenridge/424fcf186d9ddafb87d8e8d6db780f77

It does not move the cover.jpg (can confirm it was fetchart downloading the album art as I mentioned above)

Following up on using the --move CLI argument instead of reading from config, it does seem to be populated after the plugin is initialized. Made the following changes, so the operationcode is calculated at runtime when its needed instead of when beet is first launched in the plugin constructor:

diff --git a/beetsplug/filetote.py b/beetsplug/filetote.py
index abd2b17..a741f0d 100644
--- a/beetsplug/filetote.py
+++ b/beetsplug/filetote.py
@@ -26,7 +26,7 @@ class FiletotePlugin(BeetsPlugin):
             }
         )
 
-        self.operation = self._operation_type()
+        self._log.info(f"in constructor: {self.operation}")
 
         self._process_queue = []
         self._shared_artifacts = {}
@@ -86,6 +86,12 @@ class FiletotePlugin(BeetsPlugin):
 
         return operation
 
+    @property
+    def operation(self):
+        op = self._operation_type()
+        self._log.info(f"operation type {op}")
+        return op
+
     def _destination(self, filename, mapping, paired=False):
         # pylint: disable=too-many-locals
         """Returns a destination path a file should be moved to. The filename

Verbose log generated while importing with beet import --move --noincremental --from-scratch ./Folder and:

import:
  write: yes #  write ID3/data to files
  copy: no # by default dont move files
  incremental: yes
  incremental_skip_later: yes

https://gist.github.com/seanbreckenridge/31762dedd9bb25f2fbbea4e72bc95fa1

in particular interesting is at the top in the constructor its none: https://gist.github.com/seanbreckenridge/31762dedd9bb25f2fbbea4e72bc95fa1#file-1672199307-log-L4

but then when filetote checks for it later when moving the image it is correctly detected as move:

https://gist.github.com/seanbreckenridge/31762dedd9bb25f2fbbea4e72bc95fa1#file-1672199307-log-L393

something like that would solve my issue -- it correctly moves the cover.jpg file and doesn't error

apologies for the spam of notifications, am new to beets so wasn't sure how deep I'd be able to get into this

@seanbreckenridge this information was super helpful. You were right that this issue arises from differences in the plugin being initialized and beets lifecycle when the CLI option is applied. I've pulled through changes here to fix: #36

Once through and updated, I'll update this issue with confirmation of the release on PyPi.

Great, thanks!

@seanbreckenridge beets-filetote version 0.3.2 is now on PyPi and should resolve this issue.