Update of /cvsroot/freevo/kaa/metadata/src/audio
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14035/src/audio
Added Files:
.cvsignore __init__.py ac3info.py eyed3info.py flacinfo.py
id3.py m4ainfo.py ogginfo.py pcminfo.py webradioinfo.py
Log Message:
move current mmpython cvs to kaa.metadata
--- NEW FILE: .cvsignore ---
*.pyc *.pyo
--- NEW FILE: flacinfo.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# flacinfo.py - flac file parser
# -----------------------------------------------------------------------------
# $Id: flacinfo.py,v 1.1 2005/07/02 16:33:10 dischi Exp $
#
# -----------------------------------------------------------------------------
# kaa-Metadata - Media Metadata for Python
# Copyright (C) 2003-2005 Thomas Schueppel, Dirk Meyer
#
# First Edition: Thomas Schueppel <[EMAIL PROTECTED]>
# Maintainer: Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# 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 MER-
# CHANTABILITY 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
#
# -----------------------------------------------------------------------------
# python imports
import struct
import re
import logging
# kaa imports
from kaa.metadata import mediainfo
from kaa.metadata import factory
import ogginfo
# get logging object
log = logging.getLogger('metadata')
# See: http://flac.sourceforge.net/format.html
class FlacInfo(mediainfo.MusicInfo):
def __init__(self,file):
mediainfo.MusicInfo.__init__(self)
if file.read(4) != 'fLaC':
raise mediainfo.KaaMetadataParseError()
while 1:
(blockheader,) = struct.unpack('>I',file.read(4))
lastblock = (blockheader >> 31) & 1
type = (blockheader >> 24) & 0x7F
numbytes = blockheader & 0xFFFFFF
log.debug("Last?: %d, NumBytes: %d, Type: %d" % \
(lastblock, numbytes, type))
# Read this blocks the data
data = file.read(numbytes)
if type == 0:
# STREAMINFO
bits = struct.unpack('>L', data[10:14])[0]
self.samplerate = (bits >> 12) & 0xFFFFF
self.channels = ((bits >> 9) & 7) + 1
self.samplebits = ((bits >> 4) & 0x1F) + 1
md5 = data[18:34]
elif type == 1:
# PADDING
pass
elif type == 2:
# APPLICATION
pass
elif type == 3:
# SEEKTABLE
pass
elif type == 4:
# VORBIS_COMMENT
skip, self.vendor = self._extractHeaderString(data)
num, = struct.unpack('<I', data[skip:skip+4])
start = skip+4
header = {}
for i in range(num):
(nextlen, s) = self._extractHeaderString(data[start:])
start += nextlen
a = re.split('=',s)
header[(a[0]).upper()]=a[1]
if header.has_key('TITLE'):
self.title = header['TITLE']
if header.has_key('ALBUM'):
self.album = header['ALBUM']
if header.has_key('ARTIST'):
self.artist = header['ARTIST']
if header.has_key('COMMENT'):
self.comment = header['COMMENT']
if header.has_key('DATE'):
self.date = header['DATE']
if header.has_key('ENCODER'):
self.encoder = header['ENCODER']
if header.has_key('TRACKNUMBER'):
self.trackno = header['TRACKNUMBER']
self.appendtable('VORBISCOMMENT', header)
elif type == 5:
# CUESHEET
pass
else:
# UNKNOWN TYPE
pass
if lastblock:
break
def _extractHeaderString(self,header):
len = struct.unpack( '<I', header[:4] )[0]
return (len+4,unicode(header[4:4+len], 'utf-8'))
factory.register( 'application/flac', ('flac',), mediainfo.TYPE_MUSIC,
FlacInfo )
--- NEW FILE: ac3info.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# ac3info.py - AC3 file parser
# -----------------------------------------------------------------------------
# $Id: ac3info.py,v 1.1 2005/07/02 16:33:10 dischi Exp $
#
# -----------------------------------------------------------------------------
# kaa-Metadata - Media Metadata for Python
# Copyright (C) 2003-2005 Thomas Schueppel, Dirk Meyer
#
# First Edition: Thomas Schueppel <[EMAIL PROTECTED]>
# Maintainer: Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# 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 MER-
# CHANTABILITY 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
#
# -----------------------------------------------------------------------------
# python imports
import struct
# kaa imports
from kaa.metadata import mediainfo
from kaa.metadata import factory
# http://www.atsc.org/standards/a_52a.pdf
# fscod: Sample rate code, 2 bits
# 00 48
# 01 44.1
# 10 32
# 11 reserved
FSCOD = [ 48000, 44100, 32000, 0 ]
# bsmod: Bit stream mode, 3 bits
# bsmod acmod Type of Service
# 000 any main audio service: complete main (CM)
# 001 any main audio service: music and effects (ME)
# 010 any associated service: visually impaired (VI)
# 011 any associated service: hearing impaired (HI)
# 100 any associated service: dialogue (D)
# 101 any associated service: commentary (C)
# 110 any associated service: emergency (E)
# 111 001 associated service: voice over (VO)
# 111 010 - 111 main audio service: karaoke
#
# acmod: Audio coding mode, 3 bits
# 000 1+1 2 Ch1, Ch2
# 001 1/0 1 C
# 010 2/0 2 L, R
# 011 3/0 3 L, C, R
# 100 2/1 3 L, R, S
# 101 3/1 4 L, C, R, S
# 110 2/2 4 L, R, SL, SR
# 111 3/2 5 L, C, R, SL, SR
ACMOD = [ ( '1+1', 2, 'Ch1, Ch2' ),
( '1/0', 1, 'C' ),
( '2/0', 2, 'L, R' ),
( '3/0', 3, 'L, C, R' ),
( '2/1', 3, 'L, R, S' ),
( '3/1', 4, 'L, C, R, S' ),
( '2/2', 4, 'L, R, SL, SR' ),
( '3/2', 5, 'L, C, R, SL, SR' ) ]
# dsurmod: Dolby surround mode, 2 bits
# 00 not indicated
# 01 Not Dolby Surround encoded
# 10 Dolby Surround encoded
# 11 reserved
#
# lfeon: Low frequency effects channel on, 1 bit
# This bit has a value of 1 if the lfe (sub woofer) channel is on, and a
# value of 0 if the lfe channel is off.
#
# frmsizcod:
# byte&0x3e = 0x00 \b, 32 kbit/s
# byte&0x3e = 0x02 \b, 40 kbit/s
# byte&0x3e = 0x04 \b, 48 kbit/s
# byte&0x3e = 0x06 \b, 56 kbit/s
# byte&0x3e = 0x08 \b, 64 kbit/s
# byte&0x3e = 0x0a \b, 80 kbit/s
# byte&0x3e = 0x0c \b, 96 kbit/s
# byte&0x3e = 0x0e \b, 112 kbit/s
# byte&0x3e = 0x10 \b, 128 kbit/s
# byte&0x3e = 0x12 \b, 160 kbit/s
# byte&0x3e = 0x14 \b, 192 kbit/s
# byte&0x3e = 0x16 \b, 224 kbit/s
# byte&0x3e = 0x18 \b, 256 kbit/s
# byte&0x3e = 0x1a \b, 320 kbit/s
# byte&0x3e = 0x1c \b, 384 kbit/s
# byte&0x3e = 0x1e \b, 448 kbit/s
# byte&0x3e = 0x20 \b, 512 kbit/s
# byte&0x3e = 0x22 \b, 576 kbit/s
# byte&0x3e = 0x24 \b, 640 kbit/s
FRMSIZCOD = [ 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192,
224, 256, 320, 384, 448, 512, 576, 640 ]
class AC3Info(mediainfo.MusicInfo):
def __init__(self,file):
mediainfo.MusicInfo.__init__(self)
if file.name.endswith('.ac3'):
# when the ending is ac3, force the detection. It may not be
# necessary the the header is at the beginning but in the first
# 2000 bytes
check_length = 1000
else:
check_length = 1
for i in range(check_length):
if file.read(2) == '\x0b\x77':
break
else:
raise mediainfo.KaaMetadataParseError()
info = struct.unpack('<HBBBB',file.read(6))
self.samplerate = FSCOD[info[1] >> 6]
self.bitrate = FRMSIZCOD[(info[1] & 0x3F) >> 1] * 1000
bsmod = info[2] & 0x7
channels = ACMOD[info[3] >> 5]
acmod = info[3] >> 5
self.channels = ACMOD[acmod][1]
bits = 0
if acmod & 0x01 and not acmod == 0x01:
bits += 2
if acmod & 0x04:
bits += 2
if acmod == 0x02:
bits += 2
# info is now 5 bits of info[3] and all bits of info[4] ( == 13 bits)
# 'bits' bits (0-6) bits are information we don't need, after that,
# the bit we need is lfeon.
info = (((info[3] & 0x1F) << 8) + info[4])
# now we create the mask we need (based on 'bits')
# the bit number 13 - 'bits' is what we want to read
for i in range(13 - bits - 1):
info = info >> 1
if info & 1:
# subwover
self.channels += 1
self.codec = 'AC3'
self.mime = 'audio/ac3'
factory.register( 'audio/ac3', ('ac3',), mediainfo.TYPE_MUSIC, AC3Info )
--- NEW FILE: eyed3info.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# eyed3info.py - mp3 file parser using eyeD3
# -----------------------------------------------------------------------------
# $Id: eyed3info.py,v 1.1 2005/07/02 16:33:10 dischi Exp $
#
# -----------------------------------------------------------------------------
# kaa-Metadata - Media Metadata for Python
# Copyright (C) 2003-2005 Thomas Schueppel, Dirk Meyer
#
# First Edition: Thomas Schueppel <[EMAIL PROTECTED]>
# Maintainer: Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# 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 MER-
# CHANTABILITY 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
#
# -----------------------------------------------------------------------------
# python imports
import os
import re
import logging
import struct
# kaa imports
from kaa.metadata import mediainfo
from kaa.metadata import factory
# eyeD3 imports
from eyeD3 import tag as eyeD3_tag
from eyeD3 import frames as eyeD3_frames
import id3 as id3info
# get logging object
log = logging.getLogger('metadata')
# http://www.omniscia.org/~vivake/python/MP3Info.py
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.mime = 'audio/mp3'
if not eyeD3_tag.isMp3File(file.name):
raise mediainfo.KaaMetadataParseError()
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
raise mediainfo.KaaMetadataParseError()
except:
# The MP3 tag decoder crashed, assume the file is still
# MP3 and try to play it anyway
if log.level < 30:
log.exception('mp3 tag parsing %s failed!' % file.name)
except:
# The MP3 tag decoder crashed, assume the file is still
# MP3 and try to play it anyway
if log.level < 30:
log.exception('mp3 tag parsing %s failed!' % file.name)
if not id3:
# let's take a look at the header
s = file.read(4096)
if not s[:3] == 'ID3':
# no id3 tag header, not good
if not re.compile(r'0*\xFF\xFB\xB0\x04$').search(s):
# again, not good
if not re.compile(r'0*\xFF\xFA\xB0\x04$').search(s):
# that's it, it is no mp3 at all
raise mediainfo.KaaMetadataParseError()
try:
if id3 and id3.tag:
log.debug(id3.tag.frames)
for k, var in MP3_INFO_TABLE.items():
if id3.tag.frames[k]:
if k == 'APIC':
setattr(self, var, id3.tag.frames[k][0])
else:
setattr(self, var, 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 f.__class__ is eyeD3_frames.ImageFrame:
tab[f.header.id] = f
else:
log.debug(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:
genre = tcon
if genre is not None:
try:
self.genre = id3info.GENRE_LIST[genre]
except:
self.genre = genre
# 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 log.level < 30:
log.exception('parse error')
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')
factory.register( 'audio/mp3', ('mp3',), mediainfo.TYPE_MUSIC, eyeD3Info )
--- NEW FILE: m4ainfo.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# m4ainfo.py - m4a file parser
# -----------------------------------------------------------------------------
# $Id: m4ainfo.py,v 1.1 2005/07/02 16:33:10 dischi Exp $
#
# -----------------------------------------------------------------------------
# kaa-Metadata - Media Metadata for Python
# Copyright (C) 2003-2005 Thomas Schueppel, Dirk Meyer
#
# First Edition: Aubin Paul <[EMAIL PROTECTED]>
# Maintainer: Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# Based on a sample implementation posted to daap-dev mailing list by
# Bob Ippolito <[EMAIL PROTECTED]>
#
# 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 MER-
# CHANTABILITY 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
#
# -----------------------------------------------------------------------------
# python imports
import struct
import logging
# kaa imports
from kaa.metadata import mediainfo
from kaa.metadata import factory
# get logging object
log = logging.getLogger('metadata')
class Mpeg4(mediainfo.MusicInfo):
def __init__(self, file):
self.containerTags = ('moov', 'udta', 'trak', 'mdia', 'minf', 'dinf',
'stbl', 'meta', 'ilst', '----')
self.skipTags = {'meta':4 }
mediainfo.MusicInfo.__init__(self)
self.valid = 0
returnval = 0
while returnval == 0:
try:
self.readNextTag(file)
except ValueError:
returnval = 1
if not self.valid:
raise mediainfo.KaaMetadataParseError()
def readNextTag(self, file):
length, name = self.readInt(file), self.read(4, file)
length -= 8
if length < 0 or length > 1000:
raise ValueError, "Oops?"
if name in self.containerTags:
self.read(self.skipTags.get(name, 0), file)
data = '[container tag]'
else:
data = self.read(length, file)
if name == '\xa9nam':
self.title = data[8:]
self.valid = 1
if name == '\xa9ART':
self.artist = data[8:]
self.valid = 1
if name == '\xa9alb':
self.album = data[8:]
self.valid = 1
if name == 'trkn':
# Fix this
self.trackno = data
self.valid = 1
if name == '\xa9day':
self.year = data[8:]
self.valid = 1
if name == '\xa9too':
self.encoder = data[8:]
self.valid = 1
return 0
def read(self, b, file):
data = file.read(b)
if len(data) < b:
raise ValueError, "EOF"
return data
def readInt(self, file):
return struct.unpack('>I', self.read(4, file))[0]
factory.register( 'application/m4a', ('m4a',), mediainfo.TYPE_MUSIC,
Mpeg4 )
--- NEW FILE: ogginfo.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# ogginfo.py - ogg file parser
# -----------------------------------------------------------------------------
# $Id: ogginfo.py,v 1.1 2005/07/02 16:33:10 dischi Exp $
#
# -----------------------------------------------------------------------------
# kaa-Metadata - Media Metadata for Python
# Copyright (C) 2003-2005 Thomas Schueppel, Dirk Meyer
#
# First Edition: Thomas Schueppel <[EMAIL PROTECTED]>
# Maintainer: Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# 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 MER-
# CHANTABILITY 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
#
# -----------------------------------------------------------------------------
# python imports
import re
import os
import stat
import struct
import logging
# kaa imports
from kaa.metadata import mediainfo
from kaa.metadata import factory
# get logging object
log = logging.getLogger('metadata')
VORBIS_PACKET_INFO = '\01vorbis'
VORBIS_PACKET_HEADER = '\03vorbis'
VORBIS_PACKET_SETUP = '\05vorbis'
class OggInfo(mediainfo.MusicInfo):
def __init__(self,file):
mediainfo.MusicInfo.__init__(self)
h = file.read(4+1+1+20+1)
if h[:5] != "OggS\00":
log.info("Invalid header")
raise mediainfo.KaaMetadataParseError()
if ord(h[5]) != 2:
log.info("Invalid header type flag (trying to go ahead anyway)")
self.pageSegCount = ord(h[-1])
# Skip the PageSegCount
file.seek(self.pageSegCount,1)
h = file.read(7)
if h != VORBIS_PACKET_INFO:
log.info("Wrong vorbis header type, giving up.")
raise mediainfo.KaaMetadataParseError()
self.mime = 'application/ogg'
header = {}
info = file.read(23)
self.version, self.channels, self.samplerate, bitrate_max, \
self.bitrate, bitrate_min, blocksize, \
framing = struct.unpack('<IBIiiiBB',info[:23])
# INFO Header, read Oggs and skip 10 bytes
h = file.read(4+10+13)
if h[:4] == 'OggS':
(serial, pagesequence, checksum, numEntries) = \
struct.unpack( '<14xIIIB', h )
# skip past numEntries
file.seek(numEntries,1)
h = file.read(7)
if h != VORBIS_PACKET_HEADER:
# Not a corrent info header
return
self.encoder = self._extractHeaderString(file)
numItems = struct.unpack('<I',file.read(4))[0]
for i in range(numItems):
s = self._extractHeaderString(file)
a = re.split('=',s)
header[(a[0]).upper()]=a[1]
# Put Header fields into info fields
if header.has_key('TITLE'):
self.title = header['TITLE']
if header.has_key('ALBUM'):
self.album = header['ALBUM']
if header.has_key('ARTIST'):
self.artist = header['ARTIST']
if header.has_key('COMMENT'):
self.comment = header['COMMENT']
if header.has_key('DATE'):
self.date = header['DATE']
if header.has_key('ENCODER'):
self.encoder = header['ENCODER']
if header.has_key('TRACKNUMBER'):
self.trackno = header['TRACKNUMBER']
self.type = 'OGG Vorbis'
self.subtype = ''
self.length = self._calculateTrackLength(file)
self.appendtable('VORBISCOMMENT',header)
def _extractHeaderString(self,f):
len = struct.unpack( '<I', f.read(4) )[0]
return unicode(f.read(len), 'utf-8')
def _calculateTrackLength(self,f):
# seek to the end of the stream, to avoid scanning the whole file
if (os.stat(f.name)[stat.ST_SIZE] > 20000):
f.seek(os.stat(f.name)[stat.ST_SIZE]-10000)
# read the rest of the file into a buffer
h = f.read()
granule_position = 0
# search for each 'OggS' in h
if len(h):
idx = h.rfind('OggS')
if idx < 0:
return 0
pageSize = 0
h = h[idx+4:]
(check, type, granule_position, absPos, serial, pageN, crc, \
segs) = struct.unpack( '<BBIIIIIB', h[:23] )
if check != 0:
log.debug(h[:10])
return
log.debug("granule = %d / %d" % (granule_position, absPos))
# the last one is the one we are interested in
return (granule_position / self.samplerate)
factory.register( 'application/ogg', ('ogg',), mediainfo.TYPE_MUSIC,
OggInfo )
--- NEW FILE: id3.py ---
GENRE_LIST = {
0 : 'Blues',
1 : 'Classic Rock',
2 : 'Country',
3 : 'Dance',
4 : 'Disco',
5 : 'Funk',
6 : 'Grunge',
7 : 'Hip-Hop',
8 : 'Jazz',
9 : 'Metal',
10 : 'New Age',
11 : 'Oldies',
12 : 'Other',
13 : 'Pop',
14 : 'R&B',
15 : 'Rap',
16 : 'Reggae',
17 : 'Rock',
18 : 'Techno',
19 : 'Industrial',
20 : 'Alternative',
21 : 'Ska',
22 : 'Death Metal',
23 : 'Pranks',
24 : 'Soundtrack',
25 : 'Euro-Techno',
26 : 'Ambient',
27 : 'Trip-Hop',
28 : 'Vocal',
29 : 'Jazz+Funk',
30 : 'Fusion',
31 : 'Trance',
32 : 'Classical',
33 : 'Instrumental',
34 : 'Acid',
35 : 'House',
36 : 'Game',
37 : 'Sound Clip',
38 : 'Gospel',
39 : 'Noise',
40 : 'AlternRock',
41 : 'Bass',
42 : 'Soul',
43 : 'Punk',
44 : 'Space',
45 : 'Meditative',
46 : 'Instrumental Pop',
47 : 'Instrumental Rock',
48 : 'Ethnic',
49 : 'Gothic',
50 : 'Darkwave',
51 : 'Techno-Industrial',
52 : 'Electronic',
53 : 'Pop-Folk',
54 : 'Eurodance',
55 : 'Dream',
56 : 'Southern Rock',
57 : 'Comedy',
58 : 'Cult',
59 : 'Gangsta',
60 : 'Top 40',
61 : 'Christian Rap',
62 : 'Pop/Funk',
63 : 'Jungle',
64 : 'Native American',
65 : 'Cabaret',
66 : 'New Wave',
67 : 'Psychadelic',
68 : 'Rave',
69 : 'Showtunes',
70 : 'Trailer',
71 : 'Lo-Fi',
72 : 'Tribal',
73 : 'Acid Punk',
74 : 'Acid Jazz',
75 : 'Polka',
76 : 'Retro',
77 : 'Musical',
78 : 'Rock & Roll',
79 : 'Hard Rock',
# The following genres are Winamp extensions
80 : 'Folk',
81 : 'Folk-Rock',
82 : 'National Folk',
83 : 'Swing',
84 : 'Fast Fusion',
85 : 'Bebob',
86 : 'Latin',
87 : 'Revival',
88 : 'Celtic',
89 : 'Bluegrass',
90 : 'Avantgarde',
91 : 'Gothic Rock',
92 : 'Progressive Rock',
93 : 'Psychedelic Rock',
94 : 'Symphonic Rock',
95 : 'Slow Rock',
96 : 'Big Band',
97 : 'Chorus',
98 : 'Easy Listening',
99 : 'Acoustic',
100 : 'Humour',
101 : 'Speech',
102 : 'Chanson',
103 : 'Opera',
104 : 'Chamber Music',
105 : 'Sonata',
106 : 'Symphony',
107 : 'Booty Bass',
108 : 'Primus',
109 : 'Porn Groove',
110 : 'Satire',
111 : 'Slow Jam',
112 : 'Club',
113 : 'Tango',
114 : 'Samba',
115 : 'Folklore',
116 : 'Ballad',
117 : 'Power Ballad',
118 : 'Rhythmic Soul',
119 : 'Freestyle',
120 : 'Duet',
121 : 'Punk Rock',
122 : 'Drum Solo',
123 : 'A capella',
124 : 'Euro-House',
125 : 'Dance Hall'
}
--- NEW FILE: webradioinfo.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# webradioinfo.py - read webradio attributes
# -----------------------------------------------------------------------------
# $Id: webradioinfo.py,v 1.1 2005/07/02 16:33:10 dischi Exp $
#
# -----------------------------------------------------------------------------
# kaa-Metadata - Media Metadata for Python
# Copyright (C) 2003-2005 Thomas Schueppel, Dirk Meyer
#
# First Edition: Thomas Schueppel <[EMAIL PROTECTED]>
# Maintainer: Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# 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 MER-
# CHANTABILITY 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
#
# -----------------------------------------------------------------------------
# python imports
import urlparse
import string
import urllib
# kaa imports
from kaa.metadata import mediainfo
from kaa.metadata import factory
# http://205.188.209.193:80/stream/1006
ICY_tags = { 'title': 'icy-name',
'genre': 'icy-genre',
'bitrate': 'icy-br',
'caption': 'icy-url',
}
class WebRadioInfo(mediainfo.MusicInfo):
def __init__(self, url):
mediainfo.MusicInfo.__init__(self)
tup = urlparse.urlsplit(url)
scheme, location, path, query, fragment = tup
if scheme != 'http':
raise mediainfo.KaaMetadataParseError()
# Open an URL Connection
fi = urllib.urlopen(url)
# grab the statusline
self.statusline = fi.readline()
try:
statuslist = string.split(self.statusline)
except ValueError:
# assume it is okay since so many servers are badly configured
statuslist = ["ICY", "200"]
if statuslist[1] != "200":
if fi:
fi.close()
raise mediainfo.KaaMetadataParseError()
self.type = 'audio'
self.subtype = 'mp3'
# grab any headers for a max of 10 lines
linecnt = 0
tab = {}
lines = fi.readlines(512)
for linecnt in range(0,11):
icyline = lines[linecnt]
icyline = icyline.rstrip('\r\n')
if len(icyline) < 4:
break
cidx = icyline.find(':')
if cidx != -1:
# break on short line (ie. really should be a blank line)
# strip leading and trailing whitespace
tab[icyline[:cidx].strip()] = icyline[cidx+2:].strip()
if fi:
fi.close()
self.appendtable('ICY', tab)
self.tag_map = { ('ICY', 'en') : ICY_tags }
# Copy Metadata from tables into the main set of attributes
for k in self.tag_map.keys():
map(lambda x:self.setitem(x,self.gettable(k[0],k[1]),
self.tag_map[k][x]),
self.tag_map[k].keys())
self.bitrate = string.atoi(self.bitrate)*1000
factory.register( 'text/plain', mediainfo.EXTENSION_STREAM,
mediainfo.TYPE_MUSIC, WebRadioInfo )
--- NEW FILE: __init__.py ---
--- NEW FILE: pcminfo.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# pcminfo.py - pcm file parser
# -----------------------------------------------------------------------------
# $Id: pcminfo.py,v 1.1 2005/07/02 16:33:10 dischi Exp $
#
# -----------------------------------------------------------------------------
# kaa-Metadata - Media Metadata for Python
# Copyright (C) 2003-2005 Thomas Schueppel, Dirk Meyer
#
# First Edition: Thomas Schueppel <[EMAIL PROTECTED]>
# Maintainer: Dirk Meyer <[EMAIL PROTECTED]>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# 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 MER-
# CHANTABILITY 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
#
# -----------------------------------------------------------------------------
# python imports
import sndhdr
# kaa imports
from kaa.metadata import mediainfo
from kaa.metadata import factory
class PCMInfo(mediainfo.AudioInfo):
def __init__(self,file):
mediainfo.AudioInfo.__init__(self)
t = self._what(file)
if not t:
raise mediainfo.KaaMetadataParseError()
(self.type, self.samplerate, self.channels, self.bitrate, \
self.samplebits) = t
if self.bitrate == -1:
# doesn't look right
raise mediainfo.KaaMetadataParseError()
self.mime = "audio/%s" % self.type
def _what(self,f):
"""Recognize sound headers"""
h = f.read(512)
for tf in sndhdr.tests:
res = tf(h, f)
if res:
return res
return None
factory.register( 'application/pcm', ('wav','aif','voc','au'),
mediainfo.TYPE_AUDIO, PCMInfo )
-------------------------------------------------------
SF.Net email is sponsored by: Discover Easy Linux Migration Strategies
from IBM. Find simple to follow Roadmaps, straightforward articles,
informative Webcasts and more! Get everything you need to get up to
speed, fast. http://ads.osdn.com/?ad_id=7477&alloc_id=16492&op=click
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog