How to use Custom Fields?

I think this has been mentioned a few times on the GitHub page, but I can’t figure out how to work it into Beets. Essentially, I have a bunch of soundtracks for Movies/Games/etc. I’ve been adding a custom tag “Source” and “Source Relation” so that I know where and how they are related. I’d like to be able to see this information and query it with beets, but I can’t since it is not natively supported, I have to either edit the source (not preferred) or make a plugin (I think?). Supposedly, I can use the MediaFile class to do so, but I can’t quite figure out a concrete way.

Here’s what I have done so far:
from beets import plugins, mediafile

class CustomFields(plugins.BeetsPlugin):
	def __init__(self):
		field = mediafile.MediaField(
			mediafile.MP3DescStorageStyle(u'Source'),
			mediafile.StorageStyle(u'SOURCE')
		)
		self.add_media_field('source', field)

You probably don’t have to write a plugin! Have you tried just setting a custom field with beet modify source=foo?

There are more tips along these lines on the “Advanced Awesomeness” page in the docs.

The custom field won’t read the existing Vorbis Comment or id2.3 comments right? I’ve mapped a portion of my library with the proper tags through Mp3Tagger, and I’d like to access those fields with beets. That was the main intention behind the plugin. I had read that the MediaFile had been made extendable and I could add on to it through this type of plugin.

Ah, yes! A plugin is necessary for that. It looks like you’re on the right track—any evidence about what’s going wrong so far?

1 Like

I suppose the issue is that I have no idea how to proceed from here on out. My end goal is to have it accessible as a search query parameter or have it listed among “beet fields”. From what I understand, I’ve essentially added a field for the MediaFile. I simply don’t know how to add that as an acceptable field in the list.

That should be all you need to do! By default, beets treats flexible attributes you define the same way as ordinary fields—so you can query them, sort by them, etc. Adding a MediaField imbues them with the additional ability to write to files.

Perhaps you’re wondering how to populate the beets database with existing values read from the files? As with other beets fields, yours will get populated at import time, or you can do it manually using the update command.

That did the trick. Did not even realize that I needed to run beet update to get the database up-to-date with the fields specified. I’m still having issues with the queries for these fields. For example, I’ve run beet ls source:Borderlands, but nothing shows up when I do so.

Second, whenever I try to re-import, I get an error “AttributeError: ‘CustomFields’ object has no attribute ‘import_stages’”.

Third, can I use the flexible attributes in the paths: config with symlinks to the actual files?

Huh! Without more information, I don’t know what to say about the query not working how you expect it to. And for the crash, we need to see the full traceback to understand what might be happening.

And yes, flexible attributes can go in your paths: configuration! I’m not sure what you mean about symlinks, though.

plugin paths:
Sending event: pluginload
inline: adding item field multidisc
inline: adding album field format
library database: D:\Music\musiclibrary.db
library directory: D:\Music
Sending event: library_opened
Sending event: import_begin
Sending event: import_task_created
D:\Music\Artists\A\amazarashi\Sora ni Utaeba
Replacing item 584: D:\Music\Artists\A\amazarashi\Sora ni Utaeba\01 - Sora ni Utaeba.flac
Sending event: database_change
Sending event: item_removed
Replacing item 585: D:\Music\Artists\A\amazarashi\Sora ni Utaeba\02 - Gekkou, Machi wo Yaku.flac
Sending event: database_change
Sending event: item_removed
Replacing item 586: D:\Music\Artists\A\amazarashi\Sora ni Utaeba\03 - Tarareba.flac
Sending event: database_change
Sending event: item_removed
Replacing item 587: D:\Music\Artists\A\amazarashi\Sora ni Utaeba\04 - Tarareba -acoustic ver.-.flac
Sending event: database_change
Sending event: item_removed
Replacing item 588: D:\Music\Artists\A\amazarashi\Sora ni Utaeba\05 - Sora ni Utaeba -instrumental-.flac
Sending event: database_change
Sending event: database_change
Sending event: item_removed
5 of 5 items replaced
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Sending event: database_change
Reimported album: added 1505278679.94, flexible attributes [] from album 85 for D:\Music\Artists\A\amazarashi\Sora ni Utaeba
Reimported item added 1505278679.94 from item 584 for D:\Music\Artists\A\amazarashi\Sora ni Utaeba\01 - Sora ni Utaeba.flac
Reimported item flexible attributes [u'source', u'sourcerelation'] from item 584 for D:\Music\Artists\A\amazarashi\Sora ni Utaeba\01 - Sora ni Utaeba.flac
Sending event: database_change
Reimported item added 1505278679.94 from item 585 for D:\Music\Artists\A\amazarashi\Sora ni Utaeba\02 - Gekkou, Machi wo Yaku.flac
Reimported item flexible attributes [] from item 585 for D:\Music\Artists\A\amazarashi\Sora ni Utaeba\02 - Gekkou, Machi wo Yaku.flac
Sending event: database_change
Reimported item added 1505278679.94 from item 586 for D:\Music\Artists\A\amazarashi\Sora ni Utaeba\03 - Tarareba.flac
Reimported item flexible attributes [] from item 586 for D:\Music\Artists\A\amazarashi\Sora ni Utaeba\03 - Tarareba.flac
Sending event: database_change
Reimported item added 1505278679.94 from item 587 for D:\Music\Artists\A\amazarashi\Sora ni Utaeba\04 - Tarareba -acoustic ver.-.flac
Reimported item flexible attributes [] from item 587 for D:\Music\Artists\A\amazarashi\Sora ni Utaeba\04 - Tarareba -acoustic ver.-.flac
Sending event: database_change
Reimported item added 1505278679.94 from item 588 for D:\Music\Artists\A\amazarashi\Sora ni Utaeba\05 - Sora ni Utaeba -instrumental-.flac
Reimported item flexible attributes [u'source', u'sourcerelation'] from item 588 for D:\Music\Artists\A\amazarashi\Sora ni Utaeba\05 - Sora ni Utaeba -instrumental-.flac
Sending event: database_change
Traceback (most recent call last):
  File "c:\python27\lib\runpy.py", line 162, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "c:\python27\lib\runpy.py", line 72, in _run_code
    exec code in run_globals
  File "c:\python27\scripts\beet.exe\__main__.py", line 9, in <module>
  File "c:\python27\lib\site-packages\beets\ui\__init__.py", line 1256, in main
    _raw_main(args)
  File "c:\python27\lib\site-packages\beets\ui\__init__.py", line 1243, in _raw_main
    subcommand.func(lib, suboptions, subargs)
  File "c:\python27\lib\site-packages\beets\ui\commands.py", line 934, in import_func
    import_files(lib, paths, query)
  File "c:\python27\lib\site-packages\beets\ui\commands.py", line 911, in import_files
    session.run()
  File "c:\python27\lib\site-packages\beets\importer.py", line 325, in run
    pl.run_parallel(QUEUE_SIZE)
  File "c:\python27\lib\site-packages\beets\util\pipeline.py", line 445, in run_parallel
    six.reraise(exc_info[0], exc_info[1], exc_info[2])
  File "c:\python27\lib\site-packages\beets\util\pipeline.py", line 312, in run
    out = self.coro.send(msg)
  File "c:\python27\lib\site-packages\beets\util\pipeline.py", line 194, in coro
    func(*(args + (task,)))
  File "c:\python27\lib\site-packages\beets\importer.py", line 1435, in plugin_stage
    func(session, task)
  File "c:\python27\lib\site-packages\beets\plugins.py", line 118, in wrapper
    assert self._log.level == logging.NOTSET
AttributeError: 'CustomFields' object has no attribute '_log'

Here’s the traceback. I was able to fix the import_stages error by adding a lie to the plugin.

Here’s the current state of the plugin:
from beets import plugins, mediafile

class CustomFields(plugins.BeetsPlugin):
	def __init__(self):
		field = mediafile.MediaField(
			mediafile.MP3DescStorageStyle(u'Source'),
			mediafile.StorageStyle(u'SOURCE')
		)
		field2 = mediafile.MediaField(
			mediafile.MP3DescStorageStyle(u'SourceRelation'),
			mediafile.StorageStyle(u'SOURCERELATION')
		)
		self.add_media_field('source', field)
		self.add_media_field('sourcerelation', field2)
		self.import_stages = [self.stage]
	def stage(self, session, task):
		print('Importing something!')

Here’s the log for what happens when I search for the field:

beet -v -v ls source:Absolute
user configuration: C:\Users\REDACTED\AppData\Roaming\beets\config.yaml
data directory: C:\Users\REDACTED\AppData\Roaming\beets
plugin paths:
Sending event: pluginload
inline: adding item field multidisc
inline: adding album field format
library database: D:\Music\musiclibrary.db
library directory: D:\Music
Sending event: library_opened
Sending event: cli_exit

I have a file with the Source field populated by “Absolute Duo”

I think what you want is a super call in the __init__ method for your plugin class.

That seems to have fixed the issue with importing/reimporting. Now the only thing that I find a bit odd is that beet update doesn’t seem to look for the flexible attribute for the purposes of querying. It seems like I have to reimport it for it to show up. If I use the info parameter, it shows all the relevant data, including the attributes I specified, so beets can definitely see the data. Any idea for this? Or is it simpler to just call a reimport on the whole library?

Huh! That’s very strange, and I don’t have an immediate explanation. It might be possible to debug by looking at your database file, if you can share it.

I can provide the database I currently have, but I already reimported the files for the purposes of accessing the fields. Now, it seems like all of them work fine.

The way I got to this point was:

  1. Create the database with basic configuration (no plugin added yet)
  2. Import files with already existing custom fields (Source, etc) (I used FLAC files)
  3. Write custom plugin as shown in above example
  4. Add plugin to config
  5. Error occurs before and after running the beet update command
  6. Fix was done by reimporting the entire library.

I didn’t test if the cause was due to needing a computer restart or if adding a new item fixes it. The current database contains the proper item attributes, so it seems fine. If the database I currently have will help, I can provide it. Perhaps it was just an issue on my end.

OK! Well, let us know if the problem crops up again.