From 08e93a5309b28af7caa409a6a25416ecbe0473fa Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Thu, 9 Feb 2012 14:35:47 -0800 Subject: [PATCH] plugin API to extend MediaFile (#324) --- beets/plugins.py | 23 +++++++++++++++++++++++ docs/changelog.rst | 2 ++ docs/plugins/index.rst | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/beets/plugins.py b/beets/plugins.py index 0a4fbf2e6..a9b2bb839 100755 --- a/beets/plugins.py +++ b/beets/plugins.py @@ -19,6 +19,8 @@ import itertools import traceback from collections import defaultdict +from beets import mediafile + PLUGIN_NAMESPACE = 'beetsplug' DEFAULT_PLUGINS = [] @@ -36,6 +38,12 @@ class BeetsPlugin(object): functionality by defining a subclass of BeetsPlugin and overriding the abstract methods defined here. """ + def __init__(self): + """Perform one-time plugin setup. There is probably no reason to + override this method. + """ + _add_media_fields(self.item_fields()) + def commands(self): """Should return a list of beets.ui.Subcommand objects for commands that should be added to beets' CLI. @@ -72,6 +80,14 @@ class BeetsPlugin(object): """ pass + def item_fields(self): + """Returns field descriptors to be added to the MediaFile class, + in the form of a dictionary whose keys are field names and whose + values are descriptor (e.g., MediaField) instances. The Library + database schema is not (currently) extended. + """ + return {} + listeners = None @classmethod @@ -246,6 +262,13 @@ def template_values(item): values[name] = unicode(func(item)) return values +def _add_media_fields(fields): + """Adds a {name: descriptor} dictionary of fields to the MediaFile + class. Called during the plugin initialization. + """ + for key, value in fields.iteritems(): + setattr(mediafile.MediaFile, key, value) + # Event dispatch. diff --git a/docs/changelog.rst b/docs/changelog.rst index 9cd3cdba5..d20bd2bd7 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -22,6 +22,8 @@ Changelog * Filename extensions are now always lower-cased when copying and moving files. * The ``inline`` plugin now prints a more comprehensible error when exceptions occur in Python snippets. +* New plugin API: plugins can now add fields to the MediaFile tag abstraction + layer. See :ref:`writing-plugins`. * A reasonable error message is now shown when the import log file cannot be opened. * Fix a bug in the ``rewrite`` plugin that broke the use of multiple rules for diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index 5d2b52f14..117db3a0d 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -301,3 +301,39 @@ that adds a ``$disc_and_track`` field:: With this plugin enabled, templates can reference ``$disc_and_track`` as they can any standard metadata field. + +Extend MediaFile +^^^^^^^^^^^^^^^^ + +`MediaFile`_ is the file tag abstraction layer that beets uses to make +cross-format metadata manipulation simple. Plugins can add fields to MediaFile +to extend the kinds of metadata that they can easily manage. + +The ``item_fields`` method on plugins should be overridden to return a +dictionary whose keys are field names and whose values are descriptor objects +that provide the field in question. The descriptors should probably be +``MediaField`` instances (defined in ``beets.mediafile``). Here's an example +plugin that provides a meaningless new field "foo":: + + from beets import mediafile, plugins, ui + class FooPlugin(plugins.BeetsPlugin): + def item_fields(self): + return { + 'foo': mediafile.MediaField( + mp3 = mediafile.StorageStyle( + 'TXXX', id3_desc=u'Foo Field'), + mp4 = mediafile.StorageStyle( + '----:com.apple.iTunes:Foo Field'), + etc = mediafile.StorageStyle('FOO FIELD') + ), + } + +Later, the plugin can manipulate this new field by saying something like +``mf.foo = 'bar'`` where ``mf`` is a ``MediaFile`` instance. + +Note that, currently, these additional fields are *only* applied to +``MediaFile`` itself. The beets library database schema and the ``Item`` class +are not extended, so the fields are second-class citizens. This may change +eventually. + +.. _MediaFile: https://github.com/sampsyo/beets/wiki/MediaFile