summaryrefslogtreecommitdiff
path: root/rapid/filemodify.py
diff options
context:
space:
mode:
Diffstat (limited to 'rapid/filemodify.py')
-rw-r--r--rapid/filemodify.py159
1 files changed, 98 insertions, 61 deletions
diff --git a/rapid/filemodify.py b/rapid/filemodify.py
index 98921ae..49a4d24 100644
--- a/rapid/filemodify.py
+++ b/rapid/filemodify.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# -*- coding: latin1 -*-
-### Copyright (C) 2011-2012 Damon Lynch <damonlynch@gmail.com>
+### Copyright (C) 2011-2014 Damon Lynch <damonlynch@gmail.com>
### 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
@@ -20,6 +20,7 @@
import os.path, fractions
import subprocess
+import hashlib
import multiprocessing
import logging
logger = multiprocessing.get_logger()
@@ -28,9 +29,14 @@ import rpdmultiprocessing as rpdmp
import rpdfile
import metadataxmp as mxmp
import subfolderfile
+import config
+import problemnotification as pn
+
+from gettext import gettext as _
WRITE_XMP_INPLACE = rpdfile.NON_RAW_IMAGE_EXTENSIONS + ['dng']
+
def lossless_rotate(jpeg):
"""using exiftran, performs a lossless, inplace translation of a jpeg, preserving time stamps"""
try:
@@ -40,17 +46,20 @@ def lossless_rotate(jpeg):
except OSError:
v = None
return v
-
+
class FileModify(multiprocessing.Process):
- def __init__(self, auto_rotate_jpeg, focal_length, results_pipe, terminate_queue,
+ def __init__(self, auto_rotate_jpeg, focal_length, verify_file,
+ refresh_md5_on_file_change, results_pipe, terminate_queue,
run_event):
multiprocessing.Process.__init__(self)
self.results_pipe = results_pipe
self.terminate_queue = terminate_queue
self.run_event = run_event
-
+
self.auto_rotate_jpeg = auto_rotate_jpeg
self.focal_length = focal_length
+ self.verify_file = verify_file
+ self.refresh_md5_on_file_change = refresh_md5_on_file_change
def check_termination_request(self):
"""
@@ -61,17 +70,17 @@ class FileModify(multiprocessing.Process):
# terminate immediately
return True
return False
-
+
def create_rational(self, value):
return '%s/%s' % (value.numerator, value.denominator)
-
+
def run(self):
-
+
download_count = 0
copy_finished = False
- while not copy_finished:
+ while not copy_finished:
logger.debug("Finished %s. Getting next task.", download_count)
-
+
data = self.results_pipe.recv()
if len(data) > 2:
rpd_file, download_count, temp_full_file_name, thumbnail_icon, thumbnail, copy_finished = data
@@ -83,61 +92,89 @@ class FileModify(multiprocessing.Process):
return None
# pause if instructed by the caller
self.run_event.wait()
-
+
if self.check_termination_request():
return None
-
- if self.auto_rotate_jpeg and rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO:
- if rpd_file.extension in rpdfile.JPEG_EXTENSIONS:
- lossless_rotate(rpd_file.temp_full_file_name)
-
- xmp_sidecar = None
- # check to see if focal length and aperture data should be manipulated
- if self.focal_length is not None and rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO:
- if subfolderfile.load_metadata(rpd_file, temp_file=True):
- a = rpd_file.metadata.aperture()
- if a == '0.0':
- logger.info("Adjusting focal length and aperture for %s (%s)", rpd_file.temp_full_file_name, rpd_file.name)
-
- new_focal_length = fractions.Fraction(self.focal_length,1)
- new_aperture = fractions.Fraction(8,1)
- if rpd_file.extension in WRITE_XMP_INPLACE:
- try:
- rpd_file.metadata["Exif.Photo.FocalLength"] = new_focal_length
- rpd_file.metadata["Exif.Photo.FNumber"] = new_aperture
- rpd_file.metadata.write(preserve_timestamps=True)
- logger.debug("Wrote new focal length and aperture to %s (%s)", rpd_file.temp_full_file_name, rpd_file.name)
- except:
- logger.error("failed to write new focal length and aperture to %s (%s)!", rpd_file.temp_full_file_name, rpd_file.name)
- else:
- # write to xmp sidecar
- xmp_sidecar = mxmp.XmpMetadataSidecar(rpd_file.temp_full_file_name)
- xmp_sidecar.set_exif_value('FocalLength', self.create_rational(new_focal_length))
- xmp_sidecar.set_exif_value('FNumber', self.create_rational(new_aperture))
- # store values in rpd_file, so they can be used in the subfolderfile process
- rpd_file.new_focal_length = new_focal_length
- rpd_file.new_aperture = new_aperture
-
- if False:
- xmp_sidecar.set_contact_url('http://www.website.net')
- xmp_sidecar.set_contact_email('user@email.com')
-
- if xmp_sidecar is not None:
- # need to write out xmp sidecar
- o = xmp_sidecar.write_xmp_sidecar()
- logger.debug("Wrote XMP sidecar file")
- logger.debug("exiv2 output: %s", o)
- rpd_file.temp_xmp_full_name = rpd_file.temp_full_file_name + '.xmp'
-
-
+
copy_succeeded = True
+ redo_md5 = False
+
+ if self.verify_file:
+ logger.debug("Verifying file %s....", rpd_file.name)
+ md5 = hashlib.md5(open(temp_full_file_name).read()).hexdigest()
+ if md5 <> rpd_file.md5:
+ logger.critical("%s file verification FAILED", rpd_file.name)
+ logger.critical("The %s did not download correctly!", rpd_file.title)
+
+ rpd_file.status = config.STATUS_DOWNLOAD_FAILED
+
+ rpd_file.add_problem(None, pn.FILE_VERIFICATION_FAILED,
+ {'filetype': rpd_file.title})
+ rpd_file.error_title = rpd_file.problem.get_title()
+ rpd_file.error_msg = _("%(problem)s\nFile: %(file)s") % \
+ {'problem': rpd_file.problem.get_problems(),
+ 'file': rpd_file.full_file_name}
+ copy_succeeded = False
+ else:
+ logger.debug("....file %s verified", rpd_file.name)
+
+
+ if copy_succeeded:
+ if self.auto_rotate_jpeg and rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO:
+ if rpd_file.extension in rpdfile.JPEG_EXTENSIONS:
+ lossless_rotate(rpd_file.temp_full_file_name)
+ redo_md5 = True
+
+ xmp_sidecar = None
+ # check to see if focal length and aperture data should be manipulated
+ if self.focal_length is not None and rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO:
+ if subfolderfile.load_metadata(rpd_file, temp_file=True):
+ a = rpd_file.metadata.aperture()
+ if a == '0.0':
+ logger.info("Adjusting focal length and aperture for %s (%s)", rpd_file.temp_full_file_name, rpd_file.name)
+
+ new_focal_length = fractions.Fraction(self.focal_length,1)
+ new_aperture = fractions.Fraction(8,1)
+ if rpd_file.extension in WRITE_XMP_INPLACE:
+ try:
+ rpd_file.metadata["Exif.Photo.FocalLength"] = new_focal_length
+ rpd_file.metadata["Exif.Photo.FNumber"] = new_aperture
+ rpd_file.metadata.write(preserve_timestamps=True)
+ redo_md5 = True
+ logger.debug("Wrote new focal length and aperture to %s (%s)", rpd_file.temp_full_file_name, rpd_file.name)
+ except:
+ logger.error("failed to write new focal length and aperture to %s (%s)!", rpd_file.temp_full_file_name, rpd_file.name)
+ else:
+ # write to xmp sidecar
+ xmp_sidecar = mxmp.XmpMetadataSidecar(rpd_file.temp_full_file_name)
+ xmp_sidecar.set_exif_value('FocalLength', self.create_rational(new_focal_length))
+ xmp_sidecar.set_exif_value('FNumber', self.create_rational(new_aperture))
+ # store values in rpd_file, so they can be used in the subfolderfile process
+ rpd_file.new_focal_length = new_focal_length
+ rpd_file.new_aperture = new_aperture
+
+
+
+ if xmp_sidecar is not None:
+ # need to write out xmp sidecar
+ o = xmp_sidecar.write_xmp_sidecar()
+ logger.debug("Wrote XMP sidecar file")
+ logger.debug("exiv2 output: %s", o)
+ rpd_file.temp_xmp_full_name = rpd_file.temp_full_file_name + '.xmp'
+
+ if self.refresh_md5_on_file_change and redo_md5:
+ rpd_file.md5 = hashlib.md5(open(temp_full_file_name).read()).hexdigest()
+
+
+
rpd_file.metadata = None #purge metadata, as it cannot be pickled
-
- self.results_pipe.send((rpdmp.CONN_PARTIAL,
+
+
+ self.results_pipe.send((rpdmp.CONN_PARTIAL,
(copy_succeeded, rpd_file, download_count,
- temp_full_file_name,
+ temp_full_file_name,
thumbnail_icon, thumbnail)))
-
- self.results_pipe.send((rpdmp.CONN_COMPLETE, None))
-
-
+
+ self.results_pipe.send((rpdmp.CONN_COMPLETE, None))
+
+