#if 0
# -----------------------------------------------------------------------
# $Id: eyed3info.py 391 2005-01-01 14:18:11Z dischi $
# -----------------------------------------------------------------------
# $Log$
# Revision 1.16 2005/01/01 14:18:11 dischi
# add samplerate, bitrate and mode
#
# Revision 1.15 2004/09/09 02:45:58 outlyer
# Add the TPOS tag for multiple disc sets.
#
# Revision 1.14 2004/07/21 18:54:36 outlyer
# Big bugfix.
#
# If a mp3 lacks a genre, then nothing after the genre parsing works. This breaks
# calculating the length, among other things because it's all in a try except, so
# self.length takes on a random value. I was getting track lengths like 20000 seconds
# until I made this change.
#
# Revision 1.13 2004/07/11 12:06:03 dischi
# add genre parsing
#
# Revision 1.12 2004/07/07 09:35:50 dischi
# remove guessing of tracknum in TCON
#
# Revision 1.11 2004/05/29 12:31:36 dischi
# try to find trackof in TCON or trackno
#
# Revision 1.10 2004/02/08 17:42:56 dischi
# reduce debug
#
# Revision 1.9 2003/08/30 09:36:22 dischi
# turn off some debug based on DEBUG
#
# Revision 1.8 2003/07/10 13:05:05 the_krow
# o id3v2 tabled added to eyed3
# o type changed to MUSIC
#
# Revision 1.7 2003/07/02 20:15:42 dischi
# return nothing, not 0
#
# Revision 1.6 2003/06/30 13:17:18 the_krow
# o Refactored mediainfo into factory, synchronizedobject
# o Parsers now register directly at mmpython not at mmpython.mediainfo
# o use mmpython.Factory() instead of mmpython.mediainfo.get_singleton()
# o Bugfix in PNG parser
# o Renamed disc.AudioInfo into disc.AudioDiscInfo
# o Renamed disc.DataInfo into disc.DataDiscInfo
#
# Revision 1.5 2003/06/30 11:38:22 dischi
# bugfix
#
# Revision 1.4 2003/06/29 18:30:14 dischi
# many many fixes
#
# Revision 1.3 2003/06/29 12:03:41 dischi
# fixed it to be _real_ eyed3 info
#
# Revision 1.2 2003/06/20 19:17:22 dischi
# remove filename again and use file.name
#
# Revision 1.1 2003/06/09 23:13:21 the_krow
# bugfix: unknown files are now resetted before trying if they are valid
# first rudimentary eyed3 mp3 parser added
#
#
# -----------------------------------------------------------------------
# MMPython - Media Metadata for Python
# Copyright (C) 2003 Thomas Schueppel, et. al
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
# Most of the code of this module was taken from Vivake Guptas mp3info
# code. Below is his copyright notice. All credit to him.
#
# Copyright (c) 2002 Vivake Gupta (vivakeATomniscia.org). All rights reserved.
# This software is maintained by Vivake (vivakeATomniscia.org) and is available at:
# http://www.omniscia.org/~vivake/python/MP3Info.py
#
#
from mmpython import mediainfo
import mmpython
from eyeD3 import tag
from eyeD3 import frames
import os
import struct
import traceback
import id3 as id3info
MP3_INFO_TABLE = { "APIC": "picture",
"LINK": "link",
"TALB": "album",
"TCOM": "composer",
"TCOP": "copyright",
"TDOR": "release",
"TYER": "date",
"TEXT": "text",
"TIT2": "title",
"TLAN": "language",
"TLEN": "length",
"TMED": "media_type",
"TPE1": "artist",
"TPE2": "artist",
"TRCK": "trackno",
"TPOS": "discs"}
_bitrates = [
[ # MPEG-2 & 2.5
[0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256,None], # Layer 1
[0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,None], # Layer 2
[0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,None] # Layer 3
],
[ # MPEG-1
[0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,None], # Layer 1
[0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,None], # Layer 2
[0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,None] # Layer 3
]
]
_samplerates = [
[ 11025, 12000, 8000, None], # MPEG-2.5
[ None, None, None, None], # reserved
[ 22050, 24000, 16000, None], # MPEG-2
[ 44100, 48000, 32000, None], # MPEG-1
]
_modes = [ "stereo", "joint stereo", "dual channel", "mono" ]
_MP3_HEADER_SEEK_LIMIT = 4096
class eyeD3Info(mediainfo.MusicInfo):
fileName = str();
fileSize = int();
def __init__(self, file, tagVersion = eyeD3_tag.ID3_ANY_VERSION):
mediainfo.MusicInfo.__init__(self)
self.fileName = file.name;
self.valid = 1
self.mime = 'audio/mp3'
if not eyeD3_tag.isMp3File(file.name):
self.valid = 0
return
id3 = None
try:
id3 = eyeD3_tag.Mp3AudioFile(file.name)
except eyeD3_tag.TagException:
try:
id3 = eyeD3_tag.Mp3AudioFile(file.name)
except eyeD3_tag.InvalidAudioFormatException:
# File is not an MP3
self.valid = 0
return
except:
# The MP3 tag decoder crashed, assume the file is still
# MP3 and try to play it anyway
if mediainfo.DEBUG:
print 'music: oops, mp3 tag parsing failed!'
print 'music: filename = "%s"' % file.name
traceback.print_exc()
except:
# The MP3 tag decoder crashed, assume the file is still
# MP3 and try to play it anyway
if mediainfo.DEBUG:
print 'music: oops, mp3 tag parsing failed!'
print 'music: filename = "%s"' % file.name
traceback.print_exc()
if not self.valid:
return
if mediainfo.DEBUG > 1:
print id3.tag.frames
try:
if id3 and id3.tag:
for k in MP3_INFO_TABLE:
if id3.tag.frames[k]:
if k == 'APIC':
pass
else:
setattr(self, MP3_INFO_TABLE[k], id3.tag.frames[k][0].text)
if id3.tag.getYear():
self.date = id3.tag.getYear()
tab = {}
for f in id3.tag.frames:
if f.__class__ is eyeD3_frames.TextFrame:
tab[f.header.id] = f.text
elif f.__class__ is eyeD3_frames.UserTextFrame:
tab[f.header.id] = f.text
elif f.__class__ is eyeD3_frames.DateFrame:
tab[f.header.id] = f.date_str
elif f.__class__ is eyeD3_frames.CommentFrame:
tab[f.header.id] = f.comment
elif f.__class__ is eyeD3_frames.URLFrame:
tab[f.header.id] = f.url
elif f.__class__ is eyeD3_frames.UserURLFrame:
tab[f.header.id] = f.url
elif mediainfo.DEBUG:
print f.__class__
self.appendtable('id3v2', tab, 'en')
if id3.tag.frames['TCON']:
genre = None
tcon = id3.tag.frames['TCON'][0].text
try:
genre = int(tcon)
except:
try:
genre = int(tcon[1:tcon.find(')')])
except ValueError:
pass
if genre is not None:
try:
self.genre = id3info.GENRE_LIST[genre]
except:
pass
# and some tools store it as trackno/trackof in TRCK
if not self['trackof'] and self['trackno'] and self['trackno'].find('/') > 0:
self['trackof'] = self['trackno'][self['trackno'].find('/')+1:]
self['trackno'] = self['trackno'][:self['trackno'].find('/')]
if id3:
self.length = id3.getPlayTime()
except:
if mediainfo.DEBUG:
traceback.print_exc()
offset, header = self._find_header(file)
if offset == -1 or header is None:
return
self._parse_header(header)
def _find_header(self, file):
file.seek(0, 0)
amount_read = 0
# see if we get lucky with the first four bytes
amt = 4
while amount_read < _MP3_HEADER_SEEK_LIMIT:
header = file.read(amt)
if len(header) < amt:
# awfully short file. just give up.
return -1, None
amount_read = amount_read + len(header)
# on the next read, grab a lot more
amt = 500
# look for the sync byte
offset = header.find(chr(255))
if offset == -1:
continue
# looks good, make sure we have the next 3 bytes after this
# because the header is 4 bytes including sync
if offset + 4 > len(header):
more = file.read(4)
if len(more) < 4:
# end of file. can't find a header
return -1, None
amount_read = amount_read + 4
header = header + more
# the sync flag is also in the next byte, the first 3 bits
# must also be set
if ord(header[offset+1]) >> 5 != 7:
continue
# ok, that's it, looks like we have the header
return amount_read - len(header) + offset, header[offset:offset+4]
# couldn't find the header
return -1, None
def _parse_header(self, header):
# http://mpgedit.org/mpgedit/mpeg_format/MP3Format.html
bytes = struct.unpack('>i', header)[0]
mpeg_version = (bytes >> 19) & 3
layer = (bytes >> 17) & 3
bitrate = (bytes >> 12) & 15
samplerate = (bytes >> 10) & 3
mode = (bytes >> 6) & 3
if mpeg_version == 0:
self.version = 2.5
elif mpeg_version == 2:
self.version = 2
elif mpeg_version == 3:
self.version = 1
else:
return
if layer > 0:
layer = 4 - layer
else:
return
self.bitrate = _bitrates[mpeg_version & 1][layer - 1][bitrate]
self.samplerate = _samplerates[mpeg_version][samplerate]
if self.bitrate is None or self.samplerate is None:
return
self.mode = _modes[mode]
self.keys.append('mode')
mmpython.registertype( 'audio/mp3', ('mp3',), mediainfo.TYPE_MUSIC, eyeD3Info )
|