# Copyright (C) 2009 Lars Wirzenius # # 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 3 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, see . # This is necessary for running under Python 2.5, which we need to # do on Debian, for now. from __future__ import with_statement import datetime import hashlib import os import subprocess import gtk import pyexiv2 import dimbola class ImportResult(dimbola.BackgroundStatus): def __init__(self, action, description, foldername, basename, thumbnail, preview, exifs, sha1): dimbola.BackgroundStatus.__init__(self, action, description) self.foldername = foldername self.basename = basename self.thumbnail = thumbnail self.preview = preview self.exifs = exifs self.sha1 = sha1 def process_result(self, mwc): db = mwc.db with db: folderid = db.find_folder(self.foldername) if not folderid: folderid = db.add_folder(self.foldername) photoid = db.add_photo(folderid, self.basename, 0, 0) db.set_sha1(photoid, self.sha1) for exifname, exifvalue in self.exifs.iteritems(): assert type(exifvalue) == str exifid = db.find_exifname(exifname) if not exifid: exifid = db.add_exifname(exifname) db.add_exif(photoid, exifid, exifvalue) db.add_thumbnail(photoid, self.thumbnail) db.add_preview(photoid, self.preview) mwc.emit('db-changed') class PreviewMaker(object): '''Create a JPEG preview of an image file.''' def __init__(self): self.dtc = dimbola.DcrawTypeCache() def make_preview(self, filename): if self.dtc.supported(filename): return self.from_raw(filename) else: return self.from_other(filename) def from_raw(self, filename): p = subprocess.Popen(['dcraw', '-c', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE) ppm, stderr = p.communicate('') if p.returncode: raise Exception('dcraw failed: exit code %s:\n%s' % (p.returncode, stderr)) return dimbola.image_data_to_image_data(ppm, 'jpeg', { 'quality': '50' }) def from_other(self, filename): pixbuf = gtk.gdk.pixbuf_new_from_file(filename) return dimbola.pixbuf_to_image_data(pixbuf, 'jpeg', { 'quality': '50' }) class ThumbnailMaker(object): '''Create a thumbnail from a preview.''' def make_thumbnail(self, preview_jpeg): pixbuf = dimbola.image_data_to_pixbuf(preview_jpeg) pixbuf = dimbola.scale_pixbuf(pixbuf, 200, 200) return dimbola.pixbuf_to_image_data(pixbuf, 'jpeg', {'quality':'50'}) class ImportPhoto(dimbola.BackgroundJob): exifs = ['Exif.Image.Make', 'Exif.Image.Model', 'Exif.Image.Orientation', 'Exif.Image.DateTime', 'Exif.Photo.ExposureTime', 'Exif.Photo.FNumber', 'Exif.Photo.ISOSpeedRatings', 'Exif.Photo.ShutterSpeedValue', 'Exif.Photo.ApertureValue', 'Exif.Photo.ExposureBiasValue', 'Exif.Photo.FocalLength', 'Exif.Photo.WhiteBalance'] def __init__(self, pathname): self.pathname = pathname self.description = 'Importing %s' % pathname def run(self): self.send_status(dimbola.BackgroundStatus('start', 'Importing %s' % os.path.basename(self.pathname))) preview_maker = PreviewMaker() preview = preview_maker.make_preview(self.pathname) thumbnail_maker = ThumbnailMaker() thumbnail = thumbnail_maker.make_thumbnail(preview) image = pyexiv2.Image(self.pathname) image.readMetadata() foldername = os.path.dirname(os.path.abspath(self.pathname)) basename = os.path.basename(self.pathname) exifs = dict() for exifname in image.exifKeys(): if exifname not in self.exifs: continue exifs[exifname] = self.get_encoded(image, exifname) assert type(exifs[exifname]) == str return ImportResult('stop', 'Imported %s' % basename, foldername, basename, thumbnail, preview, exifs, dimbola.sha1(self.pathname)) def get_encoded(self, image, key): s = image.interpretedExifValue(key) assert type(s) == str return s class ImportFiles(dimbola.Plugin): '''Import into database using background jobs.''' def __init__(self, mwc): self.mwc = mwc self.mwc.connect('setup-widgets', self.setup_widgets) def setup_widgets(self, mwc): self.chooser = mwc.widgets['import_filechooserdialog'] self.chooser.set_transient_for(mwc.widgets['window']) self.dtc = dimbola.DcrawTypeCache() photo_filter = gtk.FileFilter() photo_filter.set_name('All images') photo_filter.add_pixbuf_formats() photo_filter.add_custom(gtk.FILE_FILTER_FILENAME | gtk.FILE_FILTER_MIME_TYPE, self.filter_dcraw_known, None) self.chooser.add_filter(photo_filter) raw_filter = gtk.FileFilter() raw_filter.set_name('RAW photos') raw_filter.add_custom(gtk.FILE_FILTER_FILENAME | gtk.FILE_FILTER_MIME_TYPE, self.filter_dcraw_known, None) self.chooser.add_filter(raw_filter) all_filter = gtk.FileFilter() all_filter.set_name('All files') all_filter.add_pattern('*') self.chooser.add_filter(all_filter) def enable(self): self.mwc.add_to_menu('file_menu', 'import_menuitem', 'Import photos') def disable(self): self.mwc.remove_from_menu('file_menu', 'import_menuitem') def on_import_menuitem_activate(self, *args): self.chooser.show() response = self.chooser.run() self.chooser.hide() if response == gtk.RESPONSE_OK: for pathname in self.chooser.get_filenames(): job = ImportPhoto(pathname) self.mwc.add_bgjob(job) def filter_dcraw_known(self, filter_info, data): pathname, uri, display_name, mime_type = filter_info return self.dtc.supported(pathname)