summaryrefslogtreecommitdiff
path: root/trunk/dimbola/plugins/import_plugin.py
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/dimbola/plugins/import_plugin.py')
-rw-r--r--trunk/dimbola/plugins/import_plugin.py213
1 files changed, 213 insertions, 0 deletions
diff --git a/trunk/dimbola/plugins/import_plugin.py b/trunk/dimbola/plugins/import_plugin.py
new file mode 100644
index 0000000..e66da88
--- /dev/null
+++ b/trunk/dimbola/plugins/import_plugin.py
@@ -0,0 +1,213 @@
+# Copyright (C) 2009 Lars Wirzenius <liw@liw.fi>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+
+# 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)
+