Repository: beetbox/beets

Issue:
convert: Add id3v23 option
The `convert` plugin can be used to convert files, so that they can be copied to a media player or devices that does not have support for all file formats in the music library, e.g. if parts of my library are FLAC but my media player only supports MP3.

Similarily,  only reason to use ID3v2.3 is that certain mediaplayers and devices don't support ID3v2.4 tags. Thus, I'd be nice to use id3v24 for you local library but use the `convert` to not only convert audio data but also tag data to a format that my playback device understands.

Thus, it'd be nice to have an `id3v23` option in the `convert` plugins settings, to make sure that ID3v2.3 tags are written to all files that are copied/converted using this plugin.


Discussion:
Sounds useful!

Resolving patch:
diff --git a/beets/art.py b/beets/art.py
--- a/beets/art.py
+++ b/beets/art.py
@@ -51,7 +51,8 @@ def get_art(log, item):
 
 
 def embed_item(log, item, imagepath, maxwidth=None, itempath=None,
-               compare_threshold=0, ifempty=False, as_album=False):
+               compare_threshold=0, ifempty=False, as_album=False,
+               id3v23=None):
     """Embed an image into the item's media file.
     """
     # Conditions and filters.
@@ -80,7 +81,7 @@ def embed_item(log, item, imagepath, maxwidth=None, itempath=None,
                  image.mime_type)
         return
 
-    item.try_write(path=itempath, tags={'images': [image]})
+    item.try_write(path=itempath, tags={'images': [image]}, id3v23=id3v23)
 
 
 def embed_album(log, album, maxwidth=None, quiet=False,
diff --git a/beets/library.py b/beets/library.py
--- a/beets/library.py
+++ b/beets/library.py
@@ -611,7 +611,7 @@ def read(self, read_path=None):
 
         self.path = read_path
 
-    def write(self, path=None, tags=None):
+    def write(self, path=None, tags=None, id3v23=None):
         """Write the item's metadata to a media file.
 
         All fields in `_media_fields` are written to disk according to
@@ -623,6 +623,9 @@ def write(self, path=None, tags=None):
         `tags` is a dictionary of additional metadata the should be
         written to the file. (These tags need not be in `_media_fields`.)
 
+        `id3v23` will override the global `id3v23` config option if it is
+        set to something other than `None`.
+
         Can raise either a `ReadError` or a `WriteError`.
         """
         if path is None:
@@ -630,6 +633,9 @@ def write(self, path=None, tags=None):
         else:
             path = normpath(path)
 
+        if id3v23 is None:
+            id3v23 = beets.config['id3v23'].get(bool)
+
         # Get the data to write to the file.
         item_tags = dict(self)
         item_tags = {k: v for k, v in item_tags.items()
@@ -640,8 +646,7 @@ def write(self, path=None, tags=None):
 
         # Open the file.
         try:
-            mediafile = MediaFile(syspath(path),
-                                  id3v23=beets.config['id3v23'].get(bool))
+            mediafile = MediaFile(syspath(path), id3v23=id3v23)
         except UnreadableFileError as exc:
             raise ReadError(path, exc)
 
@@ -657,14 +662,14 @@ def write(self, path=None, tags=None):
             self.mtime = self.current_mtime()
         plugins.send('after_write', item=self, path=path)
 
-    def try_write(self, path=None, tags=None):
+    def try_write(self, *args, **kwargs):
         """Calls `write()` but catches and logs `FileOperationError`
         exceptions.
 
         Returns `False` an exception was caught and `True` otherwise.
         """
         try:
-            self.write(path, tags)
+            self.write(*args, **kwargs)
             return True
         except FileOperationError as exc:
             log.error(u"{0}", exc)
diff --git a/beetsplug/convert.py b/beetsplug/convert.py
--- a/beetsplug/convert.py
+++ b/beetsplug/convert.py
@@ -116,6 +116,7 @@ def __init__(self):
             u'pretend': False,
             u'threads': util.cpu_count(),
             u'format': u'mp3',
+            u'id3v23': u'inherit',
             u'formats': {
                 u'aac': {
                     u'command': u'ffmpeg -i $source -y -vn -acodec aac '
@@ -316,8 +317,12 @@ def convert_item(self, dest_dir, keep_new, path_formats, fmt,
             if pretend:
                 continue
 
+            id3v23 = self.config['id3v23'].as_choice([True, False, 'inherit'])
+            if id3v23 == 'inherit':
+                id3v23 = None
+
             # Write tags from the database to the converted file.
-            item.try_write(path=converted)
+            item.try_write(path=converted, id3v23=id3v23)
 
             if keep_new:
                 # If we're keeping the transcoded file, read it again (after
@@ -332,7 +337,7 @@ def convert_item(self, dest_dir, keep_new, path_formats, fmt,
                     self._log.debug(u'embedding album art from {}',
                                     util.displayable_path(album.artpath))
                     art.embed_item(self._log, item, album.artpath,
-                                   itempath=converted)
+                                   itempath=converted, id3v23=id3v23)
 
             if keep_new:
                 plugins.send('after_convert', item=item,


Test patch: