Update of /cvsroot/freevo/kaa/metadata/src
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv14035/src
Added Files:
.cvsignore __init__.py factory.py mediainfo.py table.py
version.py
Log Message:
move current mmpython cvs to kaa.metadata
--- NEW FILE: .cvsignore ---
*.pyc *.pyo
--- NEW FILE: version.py ---
# the intention of this file is to make it possible to check
# for the program using this lib if it's an up-to-date version
# "offical" version of kaa-metadata
VERSION = '0.5.0.cvs'
# latest major change (date as integer)
CHANGED = 20040629
--- NEW FILE: mediainfo.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# mediainfo.py
# -----------------------------------------------------------------------------
# $Id: mediainfo.py,v 1.1 2005/07/02 16:33:10 dischi Exp $
#
# TODO: move some code from mediainfo to media/core.py
# See image/core.py was example.
#
# -----------------------------------------------------------------------------
# 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 logging
import copy
import locale
# kaa imports
import table
LOCAL_ENCODING = locale.getpreferredencoding();
if not LOCAL_ENCODING or LOCAL_ENCODING == "ANSI_X3.4-1968":
LOCAL_ENCODING = 'latin1';
# type definitions
TYPE_NONE = 0
TYPE_AUDIO = 1
TYPE_VIDEO = 2
TYPE_IMAGE = 4
TYPE_AV = 5
TYPE_MUSIC = 6
TYPE_HYPERTEXT = 8
TYPE_MISC = 10
MEDIACORE = ['title', 'caption', 'comment', 'artist', 'size', 'type',
'subtype', 'date', 'keywords', 'country', 'language', 'url']
AUDIOCORE = ['channels', 'samplerate', 'length', 'encoder', 'codec',
'samplebits', 'bitrate', 'language']
VIDEOCORE = ['length', 'encoder', 'bitrate', 'samplerate', 'codec',
'samplebits', 'width', 'height', 'fps', 'aspect']
MUSICCORE = ['trackno', 'trackof', 'album', 'genre','discs']
AVCORE = ['length', 'encoder', 'trackno', 'trackof', 'copyright', 'product',
'genre', 'secondary genre', 'subject', 'writer', 'producer',
'cinematographer', 'production designer', 'edited by',
'costume designer', 'music by', 'studio', 'distributed by',
'rating', 'starring', 'ripped by', 'digitizing date',
'internet address', 'source form', 'medium', 'source',
'archival location', 'commisioned by', 'engineer', 'cropped',
'sharpness', 'dimensions', 'lightness', 'dots per inch',
'palette setting', 'default audio stream', 'logo url',
'watermark url', 'info url', 'banner image', 'banner url',
'infotext', 'delay']
UNPRINTABLE_KEYS = [ 'thumbnail', ]
EXTENSION_DEVICE = 'device'
EXTENSION_DIRECTORY = 'directory'
EXTENSION_STREAM = 'stream'
# get logging object
log = logging.getLogger('metadata')
class KaaMetadataParseError:
pass
class MediaInfo:
"""
MediaInfo is the base class to all Media Metadata Containers. It defines
the basic structures that handle metadata. MediaInfo and its derivates
contain a common set of metadata attributes that is listed in keys.
Specific derivates contain additional keys to the dublin core set that is
defined in MediaInfo.
MediaInfo also contains tables of addional metadata. These tables are maps
of keys to values. The keys themselves should remain in the format that is
defined by the metadata (I.E. Hex-Numbers, FOURCC, ...) and will be
translated to more readable and i18nified values by an external entity.
"""
def __init__(self):
self.keys = []
self._tables = {}
for k in MEDIACORE:
setattr(self,k,None)
self.keys.append(k)
# get media type by parsing the __class__ information
media = str(self.__class__)
media = media[media.find('kaa.metadata.') + 13:]
self.media = media[:media.find('.')]
self.keys.append('media')
def __unicode__(self):
keys = copy.copy(self.keys)
for k in UNPRINTABLE_KEYS:
if k in keys:
keys.remove(k)
result = reduce( lambda a,b: self[b] and b != u'url' and \
u'%s\n %s: %s' % \
(a, unicode(b), unicode(self[b])) or a, keys, u'' )
if log.level < 30:
try:
for i in self._tables.keys():
try:
result += unicode(self._tables[i])
except AttributeError:
pass
except AttributeError:
pass
return result
def appendtable(self, name, hashmap, language='en'):
"""
Appends a tables of additional metadata to the Object.
If such a table already exists, the given tables items are
added to the existing one.
"""
if not self._tables.has_key((name, language)):
self._tables[(name, language)] = \
table.Table(hashmap, name, language)
else:
# Append to the already existing table
for k in hashmap.keys():
self._tables[(name, language)][k] = hashmap[k]
def correct_data(self):
"""
Correct same data based on specific rules
"""
# make sure all strings are unicode
for key in self.keys:
value = getattr(self, key)
if isinstance(value, str):
setattr(self, key, unicode(value, LOCAL_ENCODING, 'replace'))
def gettable(self, name, language='en'):
"""
returns a table of the given name and language
"""
return self._tables.get((name, language), {})
def setitem(self, item, dict, key, convert_to_str=False):
"""
set item to a specific value for the dict
"""
try:
if self.__dict__.has_key(item):
if isinstance(dict[key], str):
self.__dict__[item] = unicode(dict[key])
elif convert_to_str:
self.__dict__[item] = unicode(dict[key])
else:
self.__dict__[item] = dict[key]
else:
_debug("Unknown key: %s" % item)
except:
pass
def __getitem__(self,key):
"""
get the value of 'key'
"""
if self.__dict__.has_key(key):
if isinstance(self.__dict__[key], str):
return self.__dict__[key].strip().rstrip().replace('\0', '')
return self.__dict__[key]
elif hasattr(self, key):
return getattr(self, key)
return None
def __setitem__(self, key, val):
"""
set the value of 'key' to 'val'
"""
self.__dict__[key] = val
def has_key(self, key):
"""
check if the object has a key 'key'
"""
return self.__dict__.has_key(key) or hasattr(self, key)
def __delitem__(self, key):
"""
delete informations about 'key'
"""
try:
del self.__dict__[key]
except:
pass
if hasattr(self, key):
setattr(self, key, None)
class AudioInfo(MediaInfo):
"""
Audio Tracks in a Multiplexed Container.
"""
def __init__(self):
self.keys = []
for k in AUDIOCORE:
setattr(self,k,None)
self.keys.append(k)
class MusicInfo(AudioInfo):
"""
Digital Music.
"""
def __init__(self):
MediaInfo.__init__(self)
for k in AUDIOCORE+MUSICCORE:
setattr(self,k,None)
self.keys.append(k)
def correct_data(self):
"""
correct trackof to be two digest
"""
AudioInfo.correct_data(self)
if self['trackof']:
try:
# XXX Why is this needed anyway?
if int(self['trackno']) < 10:
self['trackno'] = '0%s' % int(self['trackno'])
except:
pass
class VideoInfo(MediaInfo):
"""
Video Tracks in a Multiplexed Container.
"""
def __init__(self):
self.keys = []
for k in VIDEOCORE:
setattr(self,k,None)
self.keys.append(k)
class ChapterInfo(MediaInfo):
"""
Chapter in a Multiplexed Container.
"""
def __init__(self, name, pos=0):
self.keys = ['name', 'pos']
setattr(self,'name', name)
setattr(self,'pos', pos)
class AVInfo(MediaInfo):
"""
Container for Audio and Video streams. This is the Container Type for
all media, that contain more than one stream.
"""
def __init__(self):
MediaInfo.__init__(self)
for k in AVCORE:
setattr(self,k,None)
self.keys.append(k)
self.audio = []
self.video = []
self.subtitles = []
self.chapters = []
def correct_data(self):
"""
correct length to be an int
"""
MediaInfo.correct_data(self)
if not self['length'] and len(self.video) and self.video[0]['length']:
self['length'] = self.video[0]['length']
for container in [ self ] + self.video + self.audio:
if container['length']:
container['length'] = int(container['length'])
def find_subtitles(self, filename):
"""
Search for subtitle files. Right now only VobSub is supported
"""
base = os.path.splitext(filename)[0]
if os.path.isfile(base+'.idx') and \
(os.path.isfile(base+'.sub') or os.path.isfile(base+'.rar')):
file = open(base+'.idx')
if file.readline().find('VobSub index file') > 0:
line = file.readline()
while (line):
if line.find('id') == 0:
self.subtitles.append(line[4:6])
line = file.readline()
file.close()
def __unicode__(self):
result = u'Attributes:'
result += MediaInfo.__unicode__(self)
if len(self.video) + len(self.audio) + len(self.subtitles) > 0:
result += "\n Stream list:"
if len(self.video):
result += reduce( lambda a,b: a + u' \n Video Stream:' + \
unicode(b), self.video, u'' )
if len(self.audio):
result += reduce( lambda a,b: a + u' \n Audio Stream:' + \
unicode(b), self.audio, u'' )
if len(self.subtitles):
result += reduce( lambda a,b: a + u' \n Subtitle Stream:' +\
unicode(b), self.subtitles, u'' )
if not isinstance(self.chapters, int) and len(self.chapters) > 0:
result += u'\n Chapter list:'
for i in range(len(self.chapters)):
result += u'\n %2s: "%s" %s' % \
(i+1, unicode(self.chapters[i]['name']),
self.chapters[i]['pos'])
return result
class CollectionInfo(MediaInfo):
"""
Collection of Digial Media like CD, DVD, Directory, Playlist
"""
def __init__(self):
MediaInfo.__init__(self)
self.tracks = []
self.keys.append('id')
self.id = None
def __unicode__(self):
result = MediaInfo.__unicode__(self)
result += u'\nTrack list:'
for counter in range(0,len(self.tracks)):
result += u' \nTrack %d:\n%s' % \
(counter+1, unicode(self.tracks[counter]))
return result
def appendtrack(self, track):
self.tracks.append(track)
--- NEW FILE: table.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# table.py - table handling
# -----------------------------------------------------------------------------
# $Id: table.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
#
# -----------------------------------------------------------------------------
from gettext import GNUTranslations
import os
LOCALEDIR = 'i18n'
class Table:
def __init__(self, hashmap, name, language='en'):
self.dict = hashmap
self.name = name
self.language = language
self.translations = {}
self.languages = []
self.i18ndir = os.path.join(LOCALEDIR, name.lower())
try:
self.read_translations()
except:
pass
def read_translations(self):
for filename in [x for x in os.listdir(self.i18ndir) \
if x.endswith('.mo')]:
lang = filename[:-3]
filename = os.path.join(self.i18ndir, filename)
f = open(filename, 'rb')
self.translations[lang] = GNUTranslations(f)
f.close()
self.languages = self.translations.keys()
def gettext(self, message, language = None):
try:
return self.translations[language].gettext(unicode(message))
except KeyError:
return unicode(message)
def __setitem__(self,key,value):
self.dict[key] = value
def __getitem__(self,key):
try:
return self.dict[key]
except KeyError:
return None
def getstr(self,key):
s = self[key]
try:
if s and len(unicode(s)) < 100:
return s
else:
return "Not Displayable"
except UnicodeDecodeError:
return "Not Displayable"
def has_key(self, key):
return self.dict.has_key(key)
def getEntry(self, key, language = 'en'):
pair = (self.gettext(key), unicode(self.dict[key]))
def __unicode__(self):
header = "\nTable %s (%s):" % (self.name, self.language)
result = reduce( lambda a,b: self[b] and "%s\n %s: %s" % \
(a, self.gettext(b,'en'), self.getstr(b)) or \
a, self.dict.keys(), header )
return result
def accept(self,mi):
pass
--- NEW FILE: __init__.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# kaa.metadata.__init__.py
# -----------------------------------------------------------------------------
# $Id: __init__.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.
#
# Please see the file freevo/Docs/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
#
# -----------------------------------------------------------------------------
# logging support
import logging
logger = logging.getLogger('metadata')
if not logger.level:
# level not set for kaa.metadata, set it to WARNING
logger.setLevel(logging.WARNING)
# import factory code for kaa.metadata access
from factory import *
# use network functions
USE_NETWORK = 1
--- NEW FILE: factory.py ---
# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# factory.py
# -----------------------------------------------------------------------------
# $Id: factory.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
#
# -----------------------------------------------------------------------------
__all__ = [ 'Factory', 'register', 'gettype', 'parse' ]
# python imports
import stat
import os
import urlparse
import traceback
import urllib
import logging
# kaa imports
import mediainfo
# get logging object
log = logging.getLogger('metadata')
_factory = None
def Factory():
"""
Create or return global unique factory object.
"""
global _factory
# One-time init
if _factory == None:
_factory = _Factory()
_factory.import_parser()
return _factory
def register(mimetype, extensions, type, c):
"""
Register a parser to the factory.
"""
return Factory().register(mimetype,extensions,type,c)
def gettype(mimetype, extensions):
"""
Return parser for mimetype / extensions
"""
return Factory().get(mimetype,extensions)
def parse(filename):
"""
parse a file
"""
return Factory().create(filename)
class _Factory:
"""
Abstract Factory for the creation of MediaInfo instances. The different
Methods create MediaInfo objects by parsing the given medium.
"""
def __init__(self):
self.extmap = {}
self.mimemap = {}
self.types = []
self.device_types = []
self.directory_types = []
self.stream_types = []
def import_parser(self):
"""
This functions imports all known parser.
"""
import mediainfo
import audio.ogginfo
import audio.pcminfo
import audio.m4ainfo
import audio.ac3info
import video.riffinfo
import video.mpeginfo
import video.asfinfo
import video.movinfo
import image.jpginfo
import image.pnginfo
import image.tiffinfo
import image.pilinfo
import video.vcdinfo
import video.realinfo
import video.ogminfo
import video.mkvinfo
import misc.xmlinfo
# import some disc modules (may fail)
try:
import disc.discinfo
import disc.vcdinfo
import disc.audioinfo
except ImportError:
pass
# find the best working DVD module
try:
import disc.lsdvd
except ImportError, e:
if log.level < 30:
log.error(e)
try:
import disc.dvdinfo
except ImportError, e:
if log.level < 30:
log.error(e)
# use fallback disc module
try:
import disc.datainfo
except ImportError, e:
if log.level < 30:
log.error(e)
import audio.eyed3info
import audio.webradioinfo
import audio.flacinfo
import misc.dirinfo
def create_from_file(self, file):
"""
create based on the file stream 'file
"""
# Check extension as a hint
e = os.path.splitext(file.name)[1].lower()
if e and e.startswith('.') and e[1:] in self.extmap:
log.debug("trying ext %s" % e[1:])
file.seek(0,0)
try:
return self.extmap[e[1:]][3](file)
except mediainfo.KaaMetadataParseError:
pass
except:
log.exception('parse error')
log.info('No Type found by Extension. Trying all')
for e in self.types:
log.debug('trying %s' % e[0])
file.seek(0,0)
try:
return e[3](file)
except:
pass
return None
def create_from_url(self,url):
"""
Create information for urls. This includes file:// and cd://
"""
split = urlparse.urlsplit(url)
scheme = split[0]
if scheme == 'file':
(scheme, location, path, query, fragment) = split
return self.create_from_filename(location+path)
elif scheme == 'cdda':
r = self.create_from_filename(split[4])
if r:
r.url = url
return r
elif scheme == 'http':
# Quick Hack for webradio support
# We will need some more soffisticated and generic construction
# method for this. Perhaps move file.open stuff into __init__
# instead of doing it here...
for e in self.stream_types:
log.debug('Trying %s' % e[0])
try:
return e[3](url)
except mediainfo.KaaMetadataParseError:
pass
else:
(scheme, location, path, query, fragment) = split
uhandle = urllib.urlopen(url)
mime = uhandle.info().gettype()
log.debug("Trying %s" % mime)
if self.mimemap.has_key(mime):
try:
return self.mimemap[mime][3](file)
except mediainfo.KaaMetadataParseError:
pass
# XXX Todo: Try other types
pass
def create_from_filename(self, filename):
"""
Create information for the given filename
"""
if os.path.isdir(filename):
return None
if os.path.isfile(filename):
try:
f = open(filename,'rb')
except IOError:
log.error('IOError reading %s' % filename)
return None
r = self.create_from_file(f)
f.close()
if r:
r.correct_data()
r.url = 'file://%s' % os.path.abspath(filename)
return r
return None
def create_from_device(self,devicename):
"""
Create information from the device. Currently only rom drives
are supported.
"""
for e in self.device_types:
log.debug('Trying %s' % e[0])
try:
t = e[3](devicename)
t.url = 'file://%s' % os.path.abspath(devicename)
return t
except mediainfo.KaaMetadataParseError:
pass
return None
def create_from_directory(self, dirname):
"""
Create information from the directory.
"""
for e in self.directory_types:
log.debug('Trying %s' % e[0])
try:
return e[3](dirname)
except mediainfo.KaaMetadataParseError:
pass
return None
def create(self, name):
"""
Global 'create' function. This function calls the different
'create_from_'-functions.
"""
try:
if name.find('://') > 0:
return self.create_from_url(name)
if not os.path.exists(name):
return None
try:
if (os.uname()[0] == 'FreeBSD' and \
stat.S_ISCHR(os.stat(name)[stat.ST_MODE])) \
or stat.S_ISBLK(os.stat(name)[stat.ST_MODE]):
return self.create_from_device(name)
except AttributeError:
pass
if os.path.isdir(name):
return self.create_from_directory(name)
return self.create_from_filename(name)
except:
log.exception('kaa.metadata.create error')
log.warning('Please report this bug to the Freevo mailing list')
return None
def register(self,mimetype,extensions,type,c):
"""
register the parser to kaa.metadata
"""
log.debug('%s registered' % mimetype)
tuple = (mimetype,extensions,type,c)
if extensions == mediainfo.EXTENSION_DEVICE:
self.device_types.append(tuple)
elif extensions == mediainfo.EXTENSION_DIRECTORY:
self.directory_types.append(tuple)
elif extensions == mediainfo.EXTENSION_STREAM:
self.stream_types.append(tuple)
else:
self.types.append(tuple)
for e in extensions:
self.extmap[e.lower()] = tuple
self.mimemap[mimetype] = tuple
def get(self, mimetype, extensions):
"""
return the object for mimetype/extensions or None
"""
if extensions == mediainfo.EXTENSION_DEVICE:
l = self.device_types
elif extensions == mediainfo.EXTENSION_DIRECTORY:
l = self.directory_types
elif extensions == mediainfo.EXTENSION_STREAM:
l = self.stream_types
else:
l = self.types
for info in l:
if info[0] == mimetype and info[1] == extensions:
return info[3]
return None
-------------------------------------------------------
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