1
2
3 __doc__ = """
4 This module defines a set of member classes aiming at
5 dealing with audio formats such as Vorbis, Flac, MP3, WMA, etc.
6
7 This classes will generate an entry member based on the id3 tags
8 of media resources. This means that you should be careful on
9 how those are set before using these classes.
10
11 This package will require the mutagen library that your can find at:
12 http://cheeseshop.python.org/pypi/mutagen/
13
14 And wmainfo for WMA support:
15 http://cheeseshop.python.org/pypi/wmainfo-py
16 """
17
18 from datetime import datetime
19 import os
20 from xml.sax.saxutils import escape
21
22 from bridge import Element, Attribute
23 from bridge.lib import isodate
24 from bridge.common import ATOM10_PREFIX, ATOMPUB_PREFIX, XML_PREFIX, XML_NS, \
25 ATOM10_NS, ATOMPUB_NS
26
27 from amplee.utils import generate_uuid_uri, parse_isodate, get_isodate
28 from amplee.utils import create_temporary_resource, delete_temporary_resource
29 from amplee.error import MemberMediaError
30 from amplee.atompub.member import MediaMember
31 from amplee.atompub.member.helper import MemberHelper
32
33 from mutagen.easyid3 import EasyID3
34 from mutagen.mp3 import MP3
35 from mutagen.oggvorbis import OggVorbis
36 from mutagen.oggflac import OggFLAC
37 from mutagen.flac import FLAC, FLACNoHeaderError
38 from mutagen.wavpack import WavPack
39 from mutagen.apev2 import APEv2File
40 from mutagen.m4a import M4A
41
42 try:
43 from wmainfo import WmaInfo
44 except ImportError:
45 from warnings import warn
46 warn("Couldn't import wmainfo.WmaInfo. WMA files will not be supported")
47 WmaInfo = None
48
50 - def __init__(self, collection, source, audio,
51 ext, media_type, title=u'', description=u'',
52 name_format='%A %d %n', entry_id_creator=None, **kwargs):
53 """
54 Creates a member based on ID3 tags, typically audio files such a MP3 or Vorbis
55
56 Keyword arguments
57 collection -- parent collection
58 source -- fileobject of content to handle
59 length -- amount of data to read from source
60 title -- title to use for the Atom entry. If not provided
61 will use 'title' ID3 tag.
62 description -- summary to use for the Atom entry
63 name_format -- template to generate the member and media ids
64 ext -- extension to use fo the media resource
65 media_type -- mime type of the media resource
66 entry_id_creator -- function object which will return the id
67 to use in the atom:id element (as an unicode object)
68
69 The name format defaults takes the following values:
70 %A -- artist name
71 %d -- album name
72 %t -- tracknumber
73 %n -- track title
74 %Y -- year
75 %M -- month
76 %D -- day
77 It defaults to %A %d %n
78
79 The entry_id_creator function must takes the following parameters:
80 base_uri, source, artist, album, tracknumber, title, date, genres
81 """
82 MediaMember.__init__(self, collection, media_type=media_type)
83
84 if not name_format:
85 name_format = '%A %d %n'
86
87 artist, album, tracknumber, title, date, genres = self._get_infos(audio)
88 name_format = name_format.replace('%A', artist)
89 name_format = name_format.replace('%d', album)
90 name_format = name_format.replace('%t', tracknumber)
91 name_format = name_format.replace('%n', title)
92 name_format = name_format.replace('%Y', str(date.year))
93 name_format = name_format.replace('%M', "%02d" % date.month)
94 name_format = name_format.replace('%D', "%02d" % date.day)
95
96 media_id = member_id = name_format
97 if ext:
98 media_id = u'%s.%s' % (media_id, ext)
99 if self.collection.member_extension:
100 member_id = u'%s.%s.%s' % (member_id, ext, self.collection.member_extension)
101
102 self.media_id = media_id
103 self.member_id = member_id
104
105 if callable(entry_id_creator):
106 id = entry_id_creator(self.collection.base_uri, source,
107 artist, album, tracknumber, title, date, genres)
108 else:
109 id = generate_uuid_uri()
110
111 mh = MemberHelper(self.collection)
112 mh.initiate(id=id)
113 mh.add_element('title', content=title, attributes={u'type': u'text'})
114 mh.add_element('summary', unicode(description), attributes={u'type': u'text'})
115 author = mh.add_element('author')
116 mh.add_element('name', content=unicode(artist), parent=author)
117 attr = {u'rel': u'edit', u'type': self.collection.member_media_type,
118 u'href': unicode(escape("%s/%s" % (self.collection.base_edit_uri,
119 member_id))),}
120 mh.add_element('link', attributes=attr)
121
122 attr = {u'rel': u'edit-media', u'type': self.media_type,
123 u'href': unicode(escape("%s/%s" % (self.collection.base_media_edit_uri,
124 media_id)))}
125 link = mh.add_element('link', attributes=attr)
126
127 for genre in genres:
128 mh.add_element(u'category', attributes={u'term': unicode(genre)})
129
130 attr = {u'src': unicode(escape("%s/%s" % (self.collection.base_uri, media_id))),
131 u'type': self.media_type}
132 mh.add_element('content', attributes=attr)
133
134 self.entry = mh.entry
135 del mh
136
138 artist = u''
139 if 'artist' in audio:
140 artist = audio['artist'].pop()
141
142 album = u''
143 if 'album' in audio:
144 album = audio['album'].pop()
145
146 title = u''
147 if 'title' in audio:
148 title = audio['title'].pop()
149
150 tracknumber = u''
151 if 'tracknumber' in audio:
152 tracknumber = audio['tracknumber'].pop()
153
154 date = None
155 if 'date' in audio:
156 date = parse_isodate(audio['date'].pop())
157 if not date:
158 date = get_isodate()
159
160 genres = []
161 if 'genre' in audio:
162 genres = audio['genre']
163
164 return artist, album, tracknumber, title, date, genres
165
167 - def __init__(self, collection, source,
168 title=u'', description=u'',
169 name_format=None, ext='mp3',
170 media_type=u'audio/mpeg',
171 entry_id_creator=None, **kwargs):
172 fd, path, content = create_temporary_resource(source)
173 audio = MP3(path, ID3=EasyID3)
174 ID3BasedMember.__init__(self, collection, path, audio,
175 title=title, description=description,
176 name_format=name_format, ext=ext, media_type=media_type,
177 entry_id_creator=entry_id_creator, **kwargs)
178 delete_temporary_resource(path)
179
180
182 - def __init__(self, collection, source,
183 title=u'', description=u'',
184 name_format=None, ext='ogg',
185 media_type=u'application/ogg',
186 entry_id_creator=None, **kwargs):
187 fd, path, content = create_temporary_resource(source)
188 audio = OggVorbis(path)
189 ID3BasedMember.__init__(self, collection, path, audio,
190 title=title, description=description,
191 name_format=name_format, ext=ext, media_type=media_type,
192 entry_id_creator=entry_id_creator, **kwargs)
193 delete_temporary_resource(path)
194
196 - def __init__(self, collection, source,
197 title=u'', description=u'',
198 name_format=None, ext='flac',
199 media_type=u'audio/x-flac',
200 entry_id_creator=None, **kwargs):
201 fd, path, content = create_temporary_resource(source)
202 try:
203 audio = FLAC(path)
204 except FLACNoHeaderError:
205 try:
206 audio = OggFLAC(path)
207 except:
208 raise
209 ID3BasedMember.__init__(self, collection, path, audio,
210 title=title, description=description,
211 name_format=name_format, ext=ext, media_type=media_type,
212 entry_id_creator=entry_id_creator, **kwargs)
213 delete_temporary_resource(path)
214
216 - def __init__(self, collection, source,
217 title=u'', description=u'',
218 name_format=None, ext='wv',
219 media_type=u'audio/x-wav',
220 entry_id_creator=None, **kwargs):
221 fd, path, content = create_temporary_resource(source)
222 audio = APEv2File(path)
223 ID3BasedMember.__init__(self, collection, path, audio,
224 title=title, description=description,
225 name_format=name_format, ext=ext, media_type=media_type,
226 entry_id_creator=entry_id_creator, **kwargs)
227 delete_temporary_resource(path)
228
230 artist = u''
231 if 'Artist' in audio:
232 artist = unicode(audio['Artist'].value)
233
234 album = u''
235 if 'Album' in audio:
236 album = unicode(audio['Album'].value)
237
238 title = u''
239 if 'Title' in audio:
240 title = unicode(audio['Title'].value)
241
242 date = None
243 if 'Date' in audio:
244 date = parse_isodate(audio['Date'].value)
245 if not date:
246 date = datetime.now()
247
248 tracknumber = u''
249 if 'Track' in audio:
250 tracknumber = unicode(audio['Track'].value)
251
252 return artist, album, tracknumber, title, date, []
253
254
256 - def __init__(self, collection, source,
257 title=u'', description=u'',
258 name_format=None, ext='m4a',
259 media_type=u'audio/mp4a',
260 entry_id_creator=None, **kwargs):
261 fd, path, content = create_temporary_resource(source)
262 audio = M4A(path)
263 ID3BasedMember.__init__(self, collection, path, audio,
264 title=title, description=description,
265 name_format=name_format, ext=ext, media_type=media_type,
266 entry_id_creator=entry_id_creator, **kwargs)
267 delete_temporary_resource(path)
268
270 artist = u''
271 if '\xa9ART' in audio:
272 artist = audio['\xa9ART']
273
274 album = u''
275 if '\xa9alb' in audio:
276 album = audio['\xa9alb']
277
278 title = u''
279 if '\xa9nam' in audio:
280 title = audio['\xa9nam']
281
282 tracknumber = u''
283 if 'tracknumber' in audio:
284 tracknumber = [str(digit) for digit in audio['trkn']]
285 tracknumber.reverse()
286 tracknumber = ''.join(tracknumber)
287
288 genres = []
289 if '\xa9gen' in audio:
290 genres = audio['\xa9gen']
291
292 date = datetime.now()
293
294 return artist, album, tracknumber, title, date, genres
295
296
297 if WmaInfo:
299 - def __init__(self, collection, source,
300 title=u'', description=u'',
301 name_format=None, ext='wma',
302 media_type=u'audio/x-ms-wma',
303 entry_id_creator=None, **kwargs):
304 fd, path, content = create_temporary_resource(source)
305 audio = WmaInfo(path)
306 ID3BasedMember.__init__(self, collection, path, audio,
307 title=title, description=description,
308 name_format=name_format, ext=ext, media_type=media_type,
309 entry_id_creator=entry_id_creator, **kwargs)
310 delete_temporary_resource(path)
311
313 artist = unicode(audio.tags.get('Author', ''))
314 title = unicode(audio.tags.get('Title', ''))
315 tracknumber = unicode(audio.tags.get('TrackNumber', ''))
316 album = unicode(audio.tags.get('AlbumTitle', ''))
317 genres = [unicode(audio.tags.get('Genre', ''))]
318 date = audio.tags.get('Year', '')
319 date = parse_isodate(date)
320 if not date:
321 date = datetime.now()
322
323 return artist, album, tracknumber, title, date, genres
324