# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# core.py
# -----------------------------------------------------------------------------
# $Id: core.py 3659 2008-11-09 16:44:43Z dmeyer $
#
# -----------------------------------------------------------------------------
# kaa-Metadata - Media Metadata for Python
# Copyright (C) 2003-2006 Thomas Schueppel, Dirk Meyer
#
# First Edition: Thomas Schueppel <stain@acm.org>
# Maintainer: Dirk Meyer <dischi@freevo.org>
#
# Please see the file AUTHORS 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 __future__ import absolute_import
# python imports
import re
import logging
# kaa imports
#import kaa
# use strutils instead of kaa
from . import strutils as kaa
from . import fourcc
from . import language
UNPRINTABLE_KEYS = [ 'thumbnail', 'url' ]
# media type definitions
MEDIA_AUDIO = 'MEDIA_AUDIO'
MEDIA_VIDEO = 'MEDIA_VIDEO'
MEDIA_IMAGE = 'MEDIA_IMAGE'
MEDIA_AV = 'MEDIA_AV'
MEDIA_SUBTITLE = 'MEDIA_SUBTITLE'
MEDIA_CHAPTER = 'MEDIA_CHAPTER'
MEDIA_DIRECTORY = 'MEDIA_DIRECTORY'
MEDIA_DISC = 'MEDIA_DISC'
MEDIA_GAME = 'MEDIA_GAME'
MEDIACORE = ['title', 'caption', 'comment', 'size', 'type', 'subtype', 'timestamp',
'keywords', 'country', 'language', 'langcode', 'url', 'media', 'artist',
'mime']
EXTENSION_DEVICE = 'device'
EXTENSION_DIRECTORY = 'directory'
EXTENSION_STREAM = 'stream'
# get logging object
log = logging.getLogger('metadata')
[docs]class Media(object):
media = None
"""
Media is the base class to all Media Metadata Containers. It defines
the basic structures that handle metadata. Media 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 Media.
"""
_keys = MEDIACORE
table_mapping = {}
def __init__(self, hash=None):
if hash is not None:
# create Media based on dict
for key, value in hash.items():
if isinstance(value, list) and value and isinstance(value[0], dict):
value = [ Media(x) for x in value ]
self._set(key, value)
return
self._keys = self._keys[:]
self.tables = {}
for key in self._keys:
if not key == 'media':
setattr(self, key, None)
#
# unicode and string convertion for debugging
#
def __unicode__(self):
result = u''
# print normal attributes
lists = []
for key in self._keys:
value = getattr(self, key, None)
if value == None or key == 'url':
continue
if isinstance(value, list):
if value:
lists.append((key, value))
continue
if key in UNPRINTABLE_KEYS:
value = '<unprintable data, size=%d>' % len(value)
result += u'| %10s: %s\n' % (unicode(key), unicode(value))
# print lists
for key, l in lists:
for n, item in enumerate(l):
label = '+-- ' + key.rstrip('s').capitalize()
if key not in ('tracks', 'subtitles', 'chapters'):
label += ' Track'
result += u'%s #%d\n' % (label, n+1)
result += '| ' + re.sub(r'\n(.)', r'\n| \1', unicode(item))
# print tables
if log.level >= 10:
for name, table in self.tables.items():
result += '+-- Table %s\n' % str(name)
for key, value in table.items():
try:
value = unicode(value)
if len(value) > 50:
value = u'<unprintable data, size=%d>' % len(value)
except (UnicodeDecodeError, TypeError), e:
try:
value = u'<unprintable data, size=%d>' % len(value)
except AttributeError:
value = u'<unprintable data>'
result += u'| | %s: %s\n' % (unicode(key), value)
return result
def __str__(self):
return kaa.unicode_to_str(unicode(self))
def __repr__(self):
if hasattr(self, 'url'):
return '<%s %s>' % (str(self.__class__)[8:-2], self.url)
else:
return '<%s>' % (str(self.__class__)[8:-2])
#
# internal functions
#
def _appendtable(self, name, hashmap):
"""
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):
self.tables[name] = hashmap
else:
# Append to the already existing table
for k in hashmap.keys():
self.tables[name][k] = hashmap[k]
def _set(self, key, value):
"""
Set key to value and add the key to the internal keys list if
missing.
"""
if value is None and getattr(self, key, None) is None:
return
if isinstance(value, str):
value = kaa.str_to_unicode(value)
setattr(self, key, value)
if not key in self._keys:
self._keys.append(key)
def _finalize(self):
"""
Correct same data based on specific rules
"""
# make sure all strings are unicode
for key in self._keys:
if key in UNPRINTABLE_KEYS:
continue
value = getattr(self, key)
if value is None:
continue
if key == 'image':
if isinstance(value, unicode):
setattr(self, key, kaa.unicode_to_str(value))
continue
if isinstance(value, str):
setattr(self, key, kaa.str_to_unicode(value))
if isinstance(value, unicode):
setattr(self, key, value.strip().rstrip().replace(u'\0', u''))
if isinstance(value, list) and value and isinstance(value[0], Media):
for submenu in value:
submenu._finalize()
# copy needed tags from tables
for name, table in self.tables.items():
mapping = self.table_mapping.get(name, {})
for tag, attr in mapping.items():
if self.get(attr):
continue
value = table.get(tag, None)
if value is not None:
if not isinstance(value, (str, unicode)):
value = kaa.str_to_unicode(str(value))
elif isinstance(value, str):
value = kaa.str_to_unicode(value)
value = value.strip().rstrip().replace(u'\0', u'')
setattr(self, attr, value)
if 'fourcc' in self._keys and 'codec' in self._keys and self.codec is not None:
# Codec may be a fourcc, in which case we resolve it to its actual
# name and set the fourcc attribute.
self.fourcc, self.codec = fourcc.resolve(self.codec)
if 'language' in self._keys:
self.langcode, self.language = language.resolve(self.language)
#
# data access
#
def __contains__(self, key):
"""
Test if key exists in the dict
"""
return hasattr(self, key)
[docs] def get(self, attr, default = None):
"""
Returns the given attribute. If the attribute is not set by
the parser return 'default'.
"""
return getattr(self, attr, default)
def __getitem__(self, attr):
"""
Get the value of the given attribute
"""
return getattr(self, attr, None)
def __setitem__(self, key, value):
"""
Set the value of 'key' to 'value'
"""
setattr(self, key, value)
[docs] def has_key(self, key):
"""
Check if the object has an attribute 'key'
"""
return hasattr(self, key)
[docs] def convert(self):
"""
Convert Media to dict.
"""
result = {}
for k in self._keys:
value = getattr(self, k, None)
if isinstance(value, list) and value and isinstance(value[0], Media):
value = [ x.convert() for x in value ]
result[k] = value
return result
[docs] def keys(self):
"""
Return all keys for the attributes set by the parser.
"""
return self._keys
[docs]class Collection(Media):
"""
Collection of Digial Media like CD, DVD, Directory, Playlist
"""
_keys = Media._keys + [ 'id', 'tracks' ]
def __init__(self):
Media.__init__(self)
self.tracks = []