Skip to content

Commit a6de671

Browse files
committed
Merge branch 'master' of github.com:sampsyo/beets
2 parents fba8949 + 6941504 commit a6de671

File tree

6 files changed

+36
-63
lines changed

6 files changed

+36
-63
lines changed

beets/library.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -414,17 +414,23 @@ def read(self, read_path=None):
414414

415415
self.path = read_path
416416

417-
def write(self):
418-
"""Write the item's metadata to the associated file.
417+
def write(self, path=None):
418+
"""Write the item's metadata to a media file.
419+
420+
``path`` defaults to the item's path property.
419421
420422
Can raise either a `ReadError` or a `WriteError`.
421423
"""
424+
if path is None:
425+
path = self.path
426+
else:
427+
path = normpath(path)
422428
try:
423-
f = MediaFile(syspath(self.path))
429+
f = MediaFile(syspath(path))
424430
except (OSError, IOError) as exc:
425431
raise ReadError(self.path, exc)
426432

427-
plugins.send('write', item=self)
433+
plugins.send('write', item=self, path=path)
428434

429435
for key in ITEM_KEYS_WRITABLE:
430436
setattr(f, key, self[key])

beets/plugins.py

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
import logging
1818
import traceback
1919
from collections import defaultdict
20+
import inspect
2021

2122
import beets
22-
from beets import mediafile
2323

2424
PLUGIN_NAMESPACE = 'beetsplug'
2525

@@ -40,7 +40,6 @@ class BeetsPlugin(object):
4040
def __init__(self, name=None):
4141
"""Perform one-time plugin setup.
4242
"""
43-
_add_media_fields(self.item_fields())
4443
self.import_stages = []
4544
self.name = name or self.__module__.split('.')[-1]
4645
self.config = beets.config[self.name]
@@ -86,14 +85,6 @@ def item_candidates(self, item, artist, title):
8685
"""
8786
return ()
8887

89-
def item_fields(self):
90-
"""Returns field descriptors to be added to the MediaFile class,
91-
in the form of a dictionary whose keys are field names and whose
92-
values are descriptor (e.g., MediaField) instances. The Library
93-
database schema is not (currently) extended.
94-
"""
95-
return {}
96-
9788
def album_for_id(self, album_id):
9889
"""Return an AlbumInfo object or None if no matching release was
9990
found.
@@ -297,13 +288,6 @@ def template_funcs():
297288
funcs.update(plugin.template_funcs)
298289
return funcs
299290

300-
def _add_media_fields(fields):
301-
"""Adds a {name: descriptor} dictionary of fields to the MediaFile
302-
class. Called during the plugin initialization.
303-
"""
304-
for key, value in fields.iteritems():
305-
setattr(mediafile.MediaFile, key, value)
306-
307291
def import_stages():
308292
"""Get a list of import stage functions defined by plugins."""
309293
stages = []
@@ -356,4 +340,8 @@ def send(event, **arguments):
356340
Returns a list of return values from the handlers.
357341
"""
358342
log.debug('Sending event: %s' % event)
359-
return [handler(**arguments) for handler in event_handlers()[event]]
343+
for handler in event_handlers()[event]:
344+
# Don't break legacy plugins if we want to pass more arguments
345+
argspec = inspect.getargspec(handler).args
346+
args = dict((k, v) for k, v in arguments.items() if k in argspec)
347+
handler(**args)

beetsplug/convert.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,12 @@ def convert_item(dest_dir, keep_new, path_formats):
174174
encode(item.path, dest)
175175

176176
# Write tags from the database to the converted file.
177-
if not keep_new:
178-
item.path = dest
179-
item.write()
177+
item.write(path=dest)
180178

181-
# If we're keeping the transcoded file, read it again (after
182-
# writing) to get new bitrate, duration, etc.
183179
if keep_new:
180+
# If we're keeping the transcoded file, read it again (after
181+
# writing) to get new bitrate, duration, etc.
182+
item.path = dest
184183
item.read()
185184
item.store() # Store new path and audio data.
186185

docs/dev/plugins.rst

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -297,41 +297,6 @@ This field works for *item* templates. Similarly, you can register *album*
297297
template fields by adding a function accepting an ``Album`` argument to the
298298
``album_template_fields`` dict.
299299

300-
Extend MediaFile
301-
^^^^^^^^^^^^^^^^
302-
303-
`MediaFile`_ is the file tag abstraction layer that beets uses to make
304-
cross-format metadata manipulation simple. Plugins can add fields to MediaFile
305-
to extend the kinds of metadata that they can easily manage.
306-
307-
The ``item_fields`` method on plugins should be overridden to return a
308-
dictionary whose keys are field names and whose values are descriptor objects
309-
that provide the field in question. The descriptors should probably be
310-
``MediaField`` instances (defined in ``beets.mediafile``). Here's an example
311-
plugin that provides a meaningless new field "foo"::
312-
313-
from beets import mediafile, plugins, ui
314-
class FooPlugin(plugins.BeetsPlugin):
315-
def item_fields(self):
316-
return {
317-
'foo': mediafile.MediaField(
318-
mp3 = mediafile.StorageStyle(
319-
'TXXX', id3_desc=u'Foo Field'),
320-
mp4 = mediafile.StorageStyle(
321-
'----:com.apple.iTunes:Foo Field'),
322-
etc = mediafile.StorageStyle('FOO FIELD')
323-
),
324-
}
325-
326-
Later, the plugin can manipulate this new field by saying something like
327-
``mf.foo = 'bar'`` where ``mf`` is a ``MediaFile`` instance.
328-
329-
Note that, currently, these additional fields are *only* applied to
330-
``MediaFile`` itself. The beets library database schema and the ``Item`` class
331-
are not extended, so the fields are second-class citizens. This may change
332-
eventually.
333-
334-
.. _MediaFile: https://github.com/sampsyo/beets/wiki/MediaFile
335300

336301
Add Import Pipeline Stages
337302
^^^^^^^^^^^^^^^^^^^^^^^^^^

docs/reference/cli.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ Beets includes support for shell command completion. The command ``beet
361361
completion`` prints out a `bash`_ 3.2 script; to enable completion put a line
362362
like this into your ``.bashrc`` or similar file::
363363

364-
eval $(beet completion)
364+
eval "$(beet completion)"
365365

366366
Or, to avoid slowing down your shell startup time, you can pipe the ``beet
367367
completion`` output to a file and source that instead.

test/test_library.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from beets import util
3030
from beets import plugins
3131
from beets import config
32+
from beets.mediafile import MediaFile
3233

3334
TEMP_LIB = os.path.join(_common.RSRC, 'test_copy.blb')
3435

@@ -958,6 +959,20 @@ def test_no_write_permission(self):
958959
self.i.path = path
959960
self.assertRaises(beets.library.WriteError, self.i.write)
960961

962+
def test_write_with_custom_path(self):
963+
custom_path = os.path.join(self.temp_dir, 'file.mp3')
964+
self.i.path = os.path.join(self.temp_dir, 'item_file.mp3')
965+
shutil.copy(os.path.join(_common.RSRC, 'empty.mp3'), custom_path)
966+
shutil.copy(os.path.join(_common.RSRC, 'empty.mp3'), self.i.path)
967+
968+
self.i['artist'] = 'new artist'
969+
self.assertNotEqual(MediaFile(custom_path).artist, 'new artist')
970+
self.assertNotEqual(MediaFile(self.i.path).artist, 'new artist')
971+
972+
self.i.write(custom_path)
973+
self.assertEqual(MediaFile(custom_path).artist, 'new artist')
974+
self.assertNotEqual(MediaFile(self.i.path).artist, 'new artist')
975+
961976

962977
def suite():
963978
return unittest.TestLoader().loadTestsFromName(__name__)

0 commit comments

Comments
 (0)