From 2f06a508bd0e0ca7b9a5bb124b007cdb51ebd3e0 Mon Sep 17 00:00:00 2001 From: Dave Hayes Date: Mon, 28 Jan 2013 14:05:34 -0600 Subject: [PATCH 1/3] Implements support for WMA/ASF * Adds support for importing/managing .wma and .asf files * Adds support for all available ASF tag equivalents * Adds two utility methods for (un)packing embedded ASF pictures * Modifies scrub plugin to work around the lack of a delete method on ASFTags object. --- beets/mediafile.py | 161 +++++++++++++++++++++++++++++++++++++-------- beetsplug/scrub.py | 6 +- 2 files changed, 140 insertions(+), 27 deletions(-) diff --git a/beets/mediafile.py b/beets/mediafile.py index 611876280..ef2d53c58 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -34,9 +34,11 @@ import mutagen.oggvorbis import mutagen.mp4 import mutagen.flac import mutagen.monkeysaudio +import mutagen.asf import datetime import re import base64 +import struct import imghdr import os import logging @@ -72,6 +74,7 @@ TYPES = { 'ape': 'APE', 'wv': 'WavPack', 'mpc': 'Musepack', + 'asf': 'Windows Media' } @@ -139,6 +142,44 @@ def _safe_cast(out_type, val): return val +def unpack_image(data): + """ + Helper function to unpack image data from a WM/Picture tag. + + The data has the following format: + 1 byte: Picture type (0-20), see ID3 APIC frame specification at http://www.id3.org/id3v2.4.0-frames + 4 bytes: Picture data length in LE format + MIME type, null terminated UTF-16-LE string + Description, null terminated UTF-16-LE string + The image data in the given length + """ + (type, size) = struct.unpack_from(" Date: Mon, 28 Jan 2013 14:26:24 -0600 Subject: [PATCH 2/3] Safely cast ASFBoolAttributes * Modify _safe_cast to account for ASFBoolAttributes --- beets/mediafile.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/beets/mediafile.py b/beets/mediafile.py index ef2d53c58..e32626e27 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -108,8 +108,11 @@ def _safe_cast(out_type, val): return False else: try: - # Should work for strings, bools, ints: - return bool(int(val)) + if isinstance(val, mutagen.asf.ASFBoolAttribute): + return bool(val) + else: + # Should work for strings, bools, ints: + return bool(int(val)) except ValueError: return False From a1889dccc53a8960d3ac22cc164bb191341993a3 Mon Sep 17 00:00:00 2001 From: Dave Hayes Date: Tue, 29 Jan 2013 16:16:46 -0600 Subject: [PATCH 3/3] Map a few more tags to custom/nonstandard attributes * Adds TotalTracks, TotalDiscs, MusicBrainz/ASIN and * Acoustid/Fingerpint --- beets/mediafile.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/beets/mediafile.py b/beets/mediafile.py index e32626e27..dfc6a3dd5 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -886,7 +886,7 @@ class MediaFile(object): etc = [StorageStyle('TRACKTOTAL'), StorageStyle('TRACKC'), StorageStyle('TOTALTRACKS')], - asf = None, + asf = StorageStyle('TotalTracks'), ) disc = MediaField(out_type=int, mp3 = StorageStyle('TPOS', packing=packing.SLASHED, pack_pos=0), @@ -901,7 +901,7 @@ class MediaFile(object): etc = [StorageStyle('DISCTOTAL'), StorageStyle('DISCC'), StorageStyle('TOTALDISCS')], - asf = None, + asf = StorageStyle('TotalDiscs'), ) lyrics = MediaField( mp3 = StorageStyle('USLT', list_elem=False, id3_desc=u''), @@ -965,7 +965,7 @@ class MediaFile(object): mp3 = StorageStyle('TXXX', id3_desc=u'ASIN'), mp4 = StorageStyle("----:com.apple.iTunes:ASIN"), etc = StorageStyle('ASIN'), - asf = None, + asf = StorageStyle('MusicBrainz/ASIN'), ) catalognum = MediaField( mp3 = StorageStyle('TXXX', id3_desc=u'CATALOGNUMBER'), @@ -1091,7 +1091,7 @@ class MediaFile(object): mp4 = StorageStyle('----:com.apple.iTunes:Acoustid Fingerprint', as_type=str), etc = StorageStyle('ACOUSTID_FINGERPRINT'), - asf = None, + asf = StorageStyle('Acoustid/Fingerprint'), ) acoustid_id = MediaField( mp3 = StorageStyle('TXXX',