# # 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