cybertools/media/asset.py

187 lines
6.3 KiB
Python

#
# Copyright (c) 2009 Helmut Merz helmutm@cy55.de
#
# 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
#
"""
Media asset file adapter.
Authors: Johann Schimpf, Erich Seifert.
$Id$
"""
from cStringIO import StringIO
from logging import getLogger
import mimetypes
import os, re, sys
from zope.interface import implements
from cybertools.media.interfaces import IMediaAsset
from cybertools.media.piltransform import PILTransform
TRANSFORM_STATEMENT = re.compile(r"\s*(\+?)([\w]+[\w\d]*)\(([^\)]*)\)\s*")
DEFAULT_FORMATS = {
"image": "image/jpeg"
}
def parseTransformStatements(txStr):
""" Parse statements in transform chain strings."""
statements = TRANSFORM_STATEMENT.findall(txStr)
return statements
def getMimeBasetype(mimetype):
return mimetype.split("/", 1)[0]
def getMimetypeExt(mimetype):
exts = mimetypes.guess_all_extensions(mimetype)
return exts and exts[-1] or ""
class MediaAssetFile(object):
""" Class for extracting metadata from assets and to create transformed
variants using file representations in subdirectories.
"""
implements(IMediaAsset)
def __init__(self, dataPath, rules, contentType):
self.dataPath = dataPath
self.rules = rules
self.mimeType = contentType
def getData(self, variant=None):
if variant is None:
return self.getOriginalData()
path = self.getPath(variant)
if not os.path.exists(path):
getLogger('cybertools.media.asset.MediaAssetFile').warn(
'Media asset directory %r not found.' % path)
self.transform()
# return self.getOriginalData()
f = open(path, 'rb')
data = f.read()
f.close()
return data
def getImageSize(self, variant=None, data=None):
if data is None:
data = self.getData(variant)
pt = PILTransform()
pt.open(StringIO(data))
if pt.im is None:
return (0, 0)
return pt.im.size
def getContentType(self, variant=None):
contentType = self.getMimeType()
if variant is None:
return contentType
outputFormat = None
# Scan all statements for a defintion of an output format
optionsstr = self.rules.get(variant)
if optionsstr:
statements = parseTransformStatements(optionsstr)
for prefix, command, args in statements:
if command == "output":
outputFormat = args
break
# Return default type if no defintion was found
if not outputFormat:
baseType = getMimeBasetype(contentType)
return DEFAULT_FORMATS.get(baseType) or contentType
return outputFormat
def transform(self, rules=None):
if rules is None:
rules = self.rules
for variant, commands in rules.items():
self.createVariant(variant, commands)
def createVariant(self, variant, commands):
oldassetdir = self.getDataPath()
# get or create output directory
path = self.getPath(variant)
assetdir = os.path.dirname(path)
if not os.path.exists(assetdir):
os.makedirs(assetdir)
excInfo = None # Save info of exceptions that may occur
try:
mediaFile = PILTransform()
mediaFile.open(oldassetdir)
statements = parseTransformStatements(commands)
for prefix, command, args in statements:
if command == "rotate":
rotArgs = args.split(",")
angle = float(rotArgs[0])
resize = False
if len(rotArgs) > 1:
resize = bool(int(rotArgs[1]))
mediaFile.rotate(angle, resize)
elif command == "color":
mode = args
mediaFile.color(mode)
elif command == "crop":
dims = [float(i) for i in args.split(",")]
if dims and (2 <= len(dims) <= 4):
mediaFile.crop(*dims)
elif command == "size":
size = [int(i) for i in args.split(",")]
if size:
mediaFile.resize(*size)
outputFormat = self.getContentType(variant)
mediaFile.save(path, outputFormat)
except Exception, e:
excInfo = sys.exc_info()
# Handle exceptions that have occured during the transformation
# in order to provide information on the affected asset
if excInfo:
eType, eValue, eTraceback = excInfo # Extract exception information
raise eType("Error transforming asset '%s': %s" %
(oldassetdir, eValue)), None, eTraceback
def getPath(self, variant):
pathOrig = self.getDataPath()
dirOrig, fileOrig = os.path.split(pathOrig)
pathTx = os.path.join(dirOrig, variant, self.getName())
outputFormat = self.getContentType(variant)
#outputExt = getMimetypeExt(outputFormat)
basePath = os.path.splitext(pathTx)[0]
for ext in mimetypes.guess_all_extensions(outputFormat):
pathTx = basePath + ext
if os.path.exists(pathTx):
return pathTx
return pathTx
def getMimeType(self):
return self.mimeType
def getName(self):
return os.path.split(self.getDataPath())[1]
def getDataPath(self):
return self.dataPath
def getOriginalData(self):
#f = self.getDataPath().open()
f = open(self.getDataPath(), 'rb')
data = f.read()
f.close()
return data