source: trunk/src/testing/bin/fileServer/misc/mmpython/audio/eyed3info.py @ 4

Revision 4, 10.8 KB checked in by ajaworski, 13 years ago (diff)

Added modified SAGE sources

Line 
1#if 0
2# -----------------------------------------------------------------------
3# $Id: eyed3info.py,v 1.16 2005/01/01 14:18:11 dischi Exp $
4# -----------------------------------------------------------------------
5# $Log: eyed3info.py,v $
6# Revision 1.16  2005/01/01 14:18:11  dischi
7# add samplerate, bitrate and mode
8#
9# Revision 1.15  2004/09/09 02:45:58  outlyer
10# Add the TPOS tag for multiple disc sets.
11#
12# Revision 1.14  2004/07/21 18:54:36  outlyer
13# Big bugfix.
14#
15# If a mp3 lacks a genre, then nothing after the genre parsing works. This breaks
16# calculating the length, among other things because it's all in a try except, so
17# self.length takes on a random value. I was getting track lengths like 20000 seconds
18# until I made this change.
19#
20# Revision 1.13  2004/07/11 12:06:03  dischi
21# add genre parsing
22#
23# Revision 1.12  2004/07/07 09:35:50  dischi
24# remove guessing of tracknum in TCON
25#
26# Revision 1.11  2004/05/29 12:31:36  dischi
27# try to find trackof in TCON or trackno
28#
29# Revision 1.10  2004/02/08 17:42:56  dischi
30# reduce debug
31#
32# Revision 1.9  2003/08/30 09:36:22  dischi
33# turn off some debug based on DEBUG
34#
35# Revision 1.8  2003/07/10 13:05:05  the_krow
36# o id3v2 tabled added to eyed3
37# o type changed to MUSIC
38#
39# Revision 1.7  2003/07/02 20:15:42  dischi
40# return nothing, not 0
41#
42# Revision 1.6  2003/06/30 13:17:18  the_krow
43# o Refactored mediainfo into factory, synchronizedobject
44# o Parsers now register directly at mmpython not at mmpython.mediainfo
45# o use mmpython.Factory() instead of mmpython.mediainfo.get_singleton()
46# o Bugfix in PNG parser
47# o Renamed disc.AudioInfo into disc.AudioDiscInfo
48# o Renamed disc.DataInfo into disc.DataDiscInfo
49#
50# Revision 1.5  2003/06/30 11:38:22  dischi
51# bugfix
52#
53# Revision 1.4  2003/06/29 18:30:14  dischi
54# many many fixes
55#
56# Revision 1.3  2003/06/29 12:03:41  dischi
57# fixed it to be _real_ eyed3 info
58#
59# Revision 1.2  2003/06/20 19:17:22  dischi
60# remove filename again and use file.name
61#
62# Revision 1.1  2003/06/09 23:13:21  the_krow
63# bugfix: unknown files are now resetted before trying if they are valid
64# first rudimentary eyed3 mp3 parser added
65#
66#
67# -----------------------------------------------------------------------
68# MMPython - Media Metadata for Python
69# Copyright (C) 2003 Thomas Schueppel, et. al
70#
71# This program is free software; you can redistribute it and/or
72# modify it under the terms of the GNU General Public License as
73# published by the Free Software Foundation; either version 2 of the
74# License, or (at your option) any later version.
75#
76# This program is distributed in the hope that it will be useful,
77# but WITHOUT ANY WARRANTY; without even the implied warranty of
78# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
79# GNU General Public License for more details.
80#
81# You should have received a copy of the GNU General Public License
82# along with this program; if not, write to the Free Software
83# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
84# USA
85#
86# Most of the code of this module was taken from Vivake Guptas mp3info
87# code. Below is his copyright notice. All credit to him.
88#
89# Copyright (c) 2002 Vivake Gupta (vivakeATomniscia.org).  All rights reserved.
90# This software is maintained by Vivake (vivakeATomniscia.org) and is available at:
91#     http://www.omniscia.org/~vivake/python/MP3Info.py
92#
93#
94
95
96from mmpython import mediainfo
97import mmpython
98
99from eyeD3 import tag as eyeD3_tag
100from eyeD3 import frames as eyeD3_frames
101
102import os
103import struct
104import traceback
105import id3 as id3info
106
107MP3_INFO_TABLE = { "APIC": "picture",
108                   "LINK": "link",
109                   "TALB": "album",
110                   "TCOM": "composer",
111                   "TCOP": "copyright",
112                   "TDOR": "release",
113                   "TYER": "date",
114                   "TEXT": "text",
115                   "TIT2": "title",
116                   "TLAN": "language",
117                   "TLEN": "length",
118                   "TMED": "media_type",
119                   "TPE1": "artist",
120                   "TPE2": "artist",
121                   "TRCK": "trackno",
122                   "TPOS": "discs"}
123
124_bitrates = [
125    [ # MPEG-2 & 2.5
126        [0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256,None], # Layer 1
127        [0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,None], # Layer 2
128        [0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,None]  # Layer 3
129    ],
130
131    [ # MPEG-1
132        [0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,None], # Layer 1
133        [0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,None], # Layer 2
134        [0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,None]  # Layer 3
135    ]
136]
137
138_samplerates = [
139    [ 11025, 12000,  8000, None], # MPEG-2.5
140    [  None,  None,  None, None], # reserved
141    [ 22050, 24000, 16000, None], # MPEG-2
142    [ 44100, 48000, 32000, None], # MPEG-1
143]
144
145_modes = [ "stereo", "joint stereo", "dual channel", "mono" ]
146
147_MP3_HEADER_SEEK_LIMIT = 4096
148
149class eyeD3Info(mediainfo.MusicInfo):
150   
151   fileName       = str();
152   fileSize       = int();
153   
154   def __init__(self, file, tagVersion = eyeD3_tag.ID3_ANY_VERSION):
155      mediainfo.MusicInfo.__init__(self)
156      self.fileName = file.name;
157      self.valid = 1
158      self.mime = 'audio/mp3'
159
160      if not eyeD3_tag.isMp3File(file.name):
161         self.valid = 0
162         return
163
164      id3 = None
165      try:
166         id3 = eyeD3_tag.Mp3AudioFile(file.name)
167      except eyeD3_tag.TagException:
168         try:
169            id3 = eyeD3_tag.Mp3AudioFile(file.name)
170         except eyeD3_tag.InvalidAudioFormatException:
171            # File is not an MP3
172            self.valid = 0
173            return
174         except:
175            # The MP3 tag decoder crashed, assume the file is still
176            # MP3 and try to play it anyway
177            if mediainfo.DEBUG:
178               print 'music: oops, mp3 tag parsing failed!'
179               print 'music: filename = "%s"' % file.name
180               traceback.print_exc()
181      except:
182         # The MP3 tag decoder crashed, assume the file is still
183         # MP3 and try to play it anyway
184         if mediainfo.DEBUG:
185            print 'music: oops, mp3 tag parsing failed!'
186            print 'music: filename = "%s"' % file.name
187            traceback.print_exc()
188
189      if not self.valid:
190         return
191
192      if mediainfo.DEBUG > 1:
193         print id3.tag.frames
194      try:
195         if id3 and id3.tag:
196            for k in MP3_INFO_TABLE:
197               if id3.tag.frames[k]:
198                  if k == 'APIC':
199                     pass
200                  else:
201                     setattr(self, MP3_INFO_TABLE[k], id3.tag.frames[k][0].text)
202            if id3.tag.getYear():
203               self.date = id3.tag.getYear()
204            tab = {}
205            for f in id3.tag.frames:
206                if f.__class__ is eyeD3_frames.TextFrame:               
207                    tab[f.header.id] = f.text
208                elif f.__class__ is eyeD3_frames.UserTextFrame:
209                    tab[f.header.id] = f.text
210                elif f.__class__ is eyeD3_frames.DateFrame:
211                    tab[f.header.id] = f.date_str
212                elif f.__class__ is eyeD3_frames.CommentFrame:
213                    tab[f.header.id] = f.comment
214                elif f.__class__ is eyeD3_frames.URLFrame:
215                    tab[f.header.id] = f.url
216                elif f.__class__ is eyeD3_frames.UserURLFrame:
217                    tab[f.header.id] = f.url
218                elif mediainfo.DEBUG:
219                   print f.__class__
220            self.appendtable('id3v2', tab, 'en')
221
222            if id3.tag.frames['TCON']:
223               genre = None
224               tcon = id3.tag.frames['TCON'][0].text
225               try:
226                  genre = int(tcon)
227               except:
228                  try:
229                      genre = int(tcon[1:tcon.find(')')])
230                  except ValueError:
231                      pass
232               if genre is not None:
233                  try:
234                     self.genre = id3info.GENRE_LIST[genre]
235                  except:
236                     pass
237            # and some tools store it as trackno/trackof in TRCK
238            if not self['trackof'] and self['trackno'] and self['trackno'].find('/') > 0:
239                self['trackof'] = self['trackno'][self['trackno'].find('/')+1:]
240                self['trackno'] = self['trackno'][:self['trackno'].find('/')]
241         if id3:
242            self.length = id3.getPlayTime()
243      except:
244         if mediainfo.DEBUG:
245            traceback.print_exc()
246      offset, header = self._find_header(file)
247      if offset == -1 or header is None:
248         return
249
250      self._parse_header(header)
251     
252
253   def _find_header(self, file):
254      file.seek(0, 0)
255      amount_read = 0
256     
257      # see if we get lucky with the first four bytes
258      amt = 4
259     
260      while amount_read < _MP3_HEADER_SEEK_LIMIT:
261         header = file.read(amt)
262         if len(header) < amt:
263            # awfully short file. just give up.
264            return -1, None
265
266         amount_read = amount_read + len(header)
267         
268         # on the next read, grab a lot more
269         amt = 500
270         
271         # look for the sync byte
272         offset = header.find(chr(255))
273         if offset == -1:
274            continue
275             
276         # looks good, make sure we have the next 3 bytes after this
277         # because the header is 4 bytes including sync
278         if offset + 4 > len(header):
279            more = file.read(4)
280            if len(more) < 4:
281               # end of file. can't find a header
282               return -1, None
283            amount_read = amount_read + 4
284            header = header + more
285
286         # the sync flag is also in the next byte, the first 3 bits
287         # must also be set
288         if ord(header[offset+1]) >> 5 != 7:
289            continue
290
291         # ok, that's it, looks like we have the header
292         return amount_read - len(header) + offset, header[offset:offset+4]
293
294      # couldn't find the header
295      return -1, None
296
297
298   def _parse_header(self, header):
299      # http://mpgedit.org/mpgedit/mpeg_format/MP3Format.html
300      bytes = struct.unpack('>i', header)[0]
301      mpeg_version =    (bytes >> 19) & 3
302      layer =           (bytes >> 17) & 3
303      bitrate =         (bytes >> 12) & 15
304      samplerate =      (bytes >> 10) & 3
305      mode =            (bytes >> 6)  & 3
306
307      if mpeg_version == 0:
308         self.version = 2.5
309      elif mpeg_version == 2:
310         self.version = 2
311      elif mpeg_version == 3:
312         self.version = 1
313      else:
314         return
315
316      if layer > 0:
317         layer = 4 - layer
318      else:
319         return
320
321      self.bitrate = _bitrates[mpeg_version & 1][layer - 1][bitrate]
322      self.samplerate = _samplerates[mpeg_version][samplerate]
323
324      if self.bitrate is None or self.samplerate is None:
325         return
326
327      self.mode = _modes[mode]
328      self.keys.append('mode')
329
330
331mmpython.registertype( 'audio/mp3', ('mp3',), mediainfo.TYPE_MUSIC, eyeD3Info )
Note: See TracBrowser for help on using the repository browser.