Suggested patch for #206

22 views
Skip to first unread message

Matěj Cepl

unread,
Sep 22, 2013, 4:58:33 PM9/22/13
to jbr...@googlegroups.com
Julien Dubreuille attached a patch to the issue 206. I have added it as
a branch XMP-rating-206 in my repo at
https://gitorious.org/jbrout/jbrout.

(I have also added a commit for PEP8-ization of jbrout/jbrout/tools.py)

Please, comment on these patches. I haven't tested them yet (my computer
is currently unable to run Xorg ;)), so any objections are more than
welcome.

Best,

Matěj

Matěj Cepl

unread,
Sep 22, 2013, 4:58:34 PM9/22/13
to jbr...@googlegroups.com, Julien Dubreuille, Matěj Cepl
From: Julien Dubreuille <julien.d...@gmail.com>

What steps will reproduce the problem? (issue #206)

1. Rate the picture with Shotwell (or another application which
writes XMP-xmp:Rating, but not the EXIF Rating/RatingPercent
tags)
2. Import the rated picture into jBrout.
3. Inspect the rating of the picture in jBrout.

What is the expected output? What do you see instead?

The rating defined in Shotwell is expected to be available in
jBrout, whereas it is not.

In fact, Shotwell reads and updates only the XMP Rating tag,
whereas jBrout reads and updates only the EXIF
Rating/RatingPercent tags. (support for XMP write only was added
in the original patch for rating, bug #95 released in r314, but
I suspect it was removed by bug #129 in r335)

The logic of this patch is summarized hereafter:

- During jBrout Import/Refresh, when reading the rating from the
file,
+ look at the XMP tag first, and update the EXIF tags if needed
+ then look at the EXIF tags, and update the XMP tag if needed
- During jBrout rating, when writing the rating to the file,
+ udpate the XMP tag and the EXIF tags

Please note that JBrout.conf["synchronizeXmp"] has not been used.

Signed-off-by: Matěj Cepl <mc...@redhat.com>
---
jbrout/jbrout/tools.py | 24 ++++++++++++++++++++++--
1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/jbrout/jbrout/tools.py b/jbrout/jbrout/tools.py
index 5fb874d..28af7b2 100755
--- a/jbrout/jbrout/tools.py
+++ b/jbrout/jbrout/tools.py
@@ -289,19 +289,38 @@ class PhotoCmd(object):

self.__comment = decode(self.__info.getComment())

- if "Exif.Image.RatingPercent" in self.__info.exifKeys():
- # Read the RatingPercent key first
+ if "Xmp.xmp.Rating" in self.__info.xmpKeys():
+ # First, XMP
+ r = int(self.__info["Xmp.xmp.Rating"])
+ if r<0: r=0
+ elif r>5: r=5
+ self.__rating = r
+ if not "Exif.Image.RatingPercent" in self.__info.exifKeys() or not "Exif.Image.Rating" in self.__info.exifKeys() or self.__info["Exif.Image.Rating"] != r:
+ self.__info["Exif.Image.Rating"]=self.__rating
+ if r>=5: r=99
+ elif r>1: r=(r-1)*25
+ elif r<=0: r=0
+ self.__info["Exif.Image.RatingPercent"]=r # short, but libexiv2 should convert this
+ self.__info.writeMetadata()
+ elif "Exif.Image.RatingPercent" in self.__info.exifKeys():
+ # Then EXIF, RatingPercent key first
r = int(self.__info["Exif.Image.RatingPercent"])
if r>=99: r=5
elif r>1: r=1+r/25
elif r<=0: r=0
self.__rating = r
+ if not "Xmp.xmp.Rating" in self.__info.xmpKeys() or self.__info["Xmp.xmp.Rating"] != self.__rating:
+ self.__info["Xmp.xmp.Rating"]=self.__rating
+ self.__info.writeMetadata()
elif "Exif.Image.Rating" in self.__info.exifKeys():
# Fallback to Rating if RatingPercent is not available
r = int(self.__info["Exif.Image.Rating"])
if r<0: r=0
elif r>5: r=5
self.__rating = r
+ if not "Xmp.xmp.Rating" in self.__info.xmpKeys() or self.__info["Xmp.xmp.Rating"] != self.__rating:
+ self.__info["Xmp.xmp.Rating"]=self.__rating
+ self.__info.writeMetadata()
else:
self.__rating = None # dont touch if no rating tag was set before

@@ -507,6 +526,7 @@ isreal : %s""" % (
# /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
assert type(r)==int
self.__info["Exif.Image.Rating"]=r # short, but libexiv2 should convert this
+ self.__info["Xmp.xmp.Rating"]=r # short, but libexiv2 should convert this
if r>=5: r=99
elif r>1: r=(r-1)*25
elif r<=0: r=0
--
1.8.3.1

Matěj Cepl

unread,
Sep 22, 2013, 4:58:35 PM9/22/13
to jbr...@googlegroups.com, Matěj Cepl
Signed-off-by: Matěj Cepl <mc...@redhat.com>
---
jbrout/jbrout/tools.py | 530 +++++++++++++++++++++++++++----------------------
1 file changed, 295 insertions(+), 235 deletions(-)

diff --git a/jbrout/jbrout/tools.py b/jbrout/jbrout/tools.py
index 28af7b2..12a79e7 100755
--- a/jbrout/jbrout/tools.py
+++ b/jbrout/jbrout/tools.py
@@ -15,9 +15,11 @@

"""
MAJOR CHANGES :
- - unlike old tools, filedate is modified at each operation which modify picture
+ - unlike old tools, filedate is modified at each operation which
+ modify picture
(so tools for backup, like rsync should work)
- - always an exifdate, when no exif date : create exif date with filedate m_time
+ - always an exifdate, when no exif date : create exif date with
+ filedate m_time
(so destroyinfo leave exifdate in place)
- use only jpegtran/exiftran tools (for LOSSLESS rotation)
- api are the same than old one (but should change in the future)
@@ -25,26 +27,29 @@ MAJOR CHANGES :
- autorot only available on LINUX and Windows
- addition of transfrom command (rotate is now depricated)
"""
-import os,sys
+import os
+import sys
from jbrout import pyexiv
import pyexiv2

import time
-from datetime import datetime,timedelta
+from datetime import datetime, timedelta
from PIL import Image
import StringIO

-import string,re
-from subprocess import Popen,PIPE,call
+import string
+import re
+from subprocess import Popen, PIPE


-def ed2cd(f): #yyyy/mm/dd hh:ii:ss -> yyyymmddhhiiss
+def ed2cd(f): # yyyy/mm/dd hh:ii:ss -> yyyymmddhhiiss
if f:
- return f[:4]+f[5:7]+f[8:10]+f[11:13]+f[14:16]+f[17:19]
+ return f[:4] + f[5:7] + f[8:10] + f[11:13] + f[14:16] + f[17:19]
else:
return f

-# the second argument provides a string when using this code to provide rotation to plugins
+# the second argument provides a string when using this code to provide
+# rotation to plugins
autoTrans = {
1: ["none", "None"],
2: ["flipHorizontal", "Flip Horizontal"],
@@ -55,49 +60,52 @@ autoTrans = {
7: ["transverse", "Transverse"],
8: ["rotate270", "Rotate Right"]}

-# videoFormats=["avi","mov"] # not used yet (marc)
+# videoFormats = ["avi", "mov"] # not used yet (marc)
+
+rawFormats = ["nef", "dng", "cr2"]
+# "cr2" files are for canon RAW. Makes pyexiv2 crash 14/07/2009 works
+# with exiv2 though
+supportedFormats = ["jpg", "jpeg"] + rawFormats

-rawFormats=["nef","dng","cr2"]
-# "cr2" files are for canon RAW. Makes pyexiv2 crash 14/07/2009 works with exiv2 though
-supportedFormats=["jpg","jpeg"] + rawFormats

class CommandException(Exception):
- def __init__(self,m):
- self.args=[m]
+ def __init__(self, m):
+ self.args = [m]
+
def __str__(self):
return self.args[0]


-def decode(s, encodings=['ascii', 'utf8', 'latin1',] ):
+def decode(s, encodings=['ascii', 'utf8', 'latin1']):
""" method to decode text (tag or comment) to unicode """
- if type(s)!=unicode:
+ if type(s) != unicode:
for encoding in encodings:
try:
return s.decode(encoding)
except UnicodeDecodeError:
pass
- print " *WARNING* : no valid decoding for string '%s'"%(str([s]))
+ print " *WARNING* : no valid decoding for string '%s'" % (str([s]))
return s.decode('utf8', 'replace')
else:
return s


-# ##############################################################################################
+# ###########################################################################
class _Command:
-# ##############################################################################################
+# ###########################################################################
""" low-level access (wrapper) to external tools used in jbrout
"""
- isWin=(sys.platform[:3] == "win")
- __path =os.path.join(os.getcwdu(),u"data",u"tools")
+ isWin = (sys.platform[:3] == "win")
+ __path = os.path.join(os.getcwdu(), u"data", u"tools")

- err=""
+ err = ""
if isWin:
# set windows path
_exiftran = None
- _jpegtran = os.path.join(__path,"jpegtran.exe")
+ _jpegtran = os.path.join(__path, "jpegtran.exe")

if not os.path.isfile(_jpegtran):
- err+="jpegtran is not present in 'tools'\n"
+ err += "jpegtran is not present in 'tools'\n"

else:
# set "non windows" path (needs 'which')
@@ -105,74 +113,76 @@ class _Command:
_jpegtran = None

if not os.path.isfile(_exiftran):
- err+="exiftran is not present, please install 'exiftran'(fbida)\n"
+ err += \
+ "exiftran is not present, please install 'exiftran'(fbida)\n"

if err:
raise Exception(err)

-
-
@staticmethod
def _run(cmds):
#~ print cmds
- cmdline = str( [" ".join(cmds)] ) # to output easily (with strange chars)
+ # to output easily (with strange chars)
+ cmdline = str([" ".join(cmds)])
try:
cmds = [i.encode(sys.getfilesystemencoding()) for i in cmds]
except:
- raise CommandException( cmdline +"\n encoding trouble")
+ raise CommandException(cmdline + "\n encoding trouble")

- p = Popen(cmds, shell=False,stdout=PIPE,stderr=PIPE)
- time.sleep(0.01) # to avoid "IOError: [Errno 4] Interrupted system call"
- out = string.join(p.stdout.readlines() ).strip()
- outerr = string.join(p.stderr.readlines() ).strip()
+ p = Popen(cmds, shell=False, stdout=PIPE, stderr=PIPE)
+ # to avoid "IOError: [Errno 4] Interrupted system call"
+ time.sleep(0.01)
+ out = string.join(p.stdout.readlines()).strip()
+ outerr = string.join(p.stderr.readlines()).strip()

if "exiftran" in cmdline:
if "processing" in outerr:
# exiftran output process in stderr ;-(
- outerr=""
+ outerr = ""

if outerr:
- raise CommandException( cmdline +"\n OUTPUT ERROR:"+outerr)
+ raise CommandException(cmdline + "\n OUTPUT ERROR:" + outerr)
else:
try:
- out = out.decode("utf_8") # recupere les infos en UTF_8
+ out = out.decode("utf_8") # retrieve information in UTF_8
except:
try:
- out = out.decode("latin_1") # recupere les anciens infos (en latin_1)
+ # retrieve information in the old encoding (Latin_1)
+ out = out.decode("latin_1")
except UnicodeDecodeError:
try:
out = out.decode(sys.getfilesystemencoding())
except UnicodeDecodeError:
- raise CommandException( cmdline +"\n decoding trouble")
+ raise CommandException(cmdline + "\n decoding trouble")
+
+ return out # unicode

- return out #unicode

class PhotoCmd(object):

file = property(lambda self: self.__file)
- exifdate = property(lambda self:self.__exifdate)
- filedate = property(lambda self:self.__filedate)
- readonly = property(lambda self:self.__readonly)
- isflash = property(lambda self:self.__isflash)
- resolution = property(lambda self:self.__resolution)
- comment = property(lambda self:self.__comment)
- rating = property(lambda self:self.__rating)
- tags = property(lambda self:self.__tags)
- isreal = property(lambda self:self.__isreal)
+ exifdate = property(lambda self: self.__exifdate)
+ filedate = property(lambda self: self.__filedate)
+ readonly = property(lambda self: self.__readonly)
+ isflash = property(lambda self: self.__isflash)
+ resolution = property(lambda self: self.__resolution)
+ comment = property(lambda self: self.__comment)
+ rating = property(lambda self: self.__rating)
+ tags = property(lambda self: self.__tags)
+ isreal = property(lambda self: self.__isreal)

# static
- format="p%Y%m%d_%H%M%S"
+ format = "p%Y%m%d_%H%M%S"

- def debug(self,m):
+ def debug(self, m):
print m

- def __init__(self,file,needAutoRename=False,needAutoRotation=False):
- assert type(file)==unicode
+ def __init__(self, file, needAutoRename=False, needAutoRotation=False):
+ assert type(file) == unicode
assert os.path.isfile(file)

-
self.__file = file
- self.__readonly = not os.access( self.__file, os.W_OK)
+ self.__readonly = not os.access(self.__file, os.W_OK)

# pre-read

@@ -180,31 +190,37 @@ class PhotoCmd(object):
self.__info.readMetadata()

if self.readonly:
- self.debug( "*WARNING* File %s is READONLY" % file )
+ self.debug("*WARNING* File %s is READONLY" % file)
else:
#-----------------------------------------------------------
# try to correct exif date if wrong
#-----------------------------------------------------------
# if no exifdate ---> put the filedate in exifdate
- # SO exifdate=filedate FOR ALL
+ # SO exifdate = filedate FOR ALL
if "Exif.Photo.DateTimeOriginal" in self.__info.exifKeys():
try:
- #self.__info["Exif.Image.DateTime"].strftime("%Y%m%d%H%M%S")
- self.__info["Exif.Photo.DateTimeOriginal"].strftime("%Y%m%d%H%M%S")
- isDateExifOk=True
- except AttributeError: # content of tag exif DateTimeOriginal is not a datetime
- isDateExifOk=False
+ #self.__info[
+ # "Exif.Image.DateTime"].strftime("%Y%m%d%H%M%S")
+ self.__info[
+ "Exif.Photo.DateTimeOriginal"].strftime("%Y%m%d%H%M%S")
+ isDateExifOk = True
+ # content of tag exif DateTimeOriginal is not a datetime
+ except AttributeError:
+ isDateExifOk = False
else: # tag exif DateTimeOriginal not present
- isDateExifOk=False
+ isDateExifOk = False

if not isDateExifOk:
- self.debug( "*WARNING* File %s had wrong exif date -> corrected" % file )
-
- fd=datetime.fromtimestamp(os.stat(file).st_mtime)
- self.__info["Exif.Image.Make"]="jBrout" # mark exif made by jbrout
- self.__info["Exif.Image.DateTime"]=fd
- self.__info["Exif.Photo.DateTimeOriginal"]=fd
- self.__info["Exif.Photo.DateTimeDigitized"]=fd
+ self.debug(
+ "*WARNING* File %s had wrong exif date -> corrected"
+ % file)
+
+ fd = datetime.fromtimestamp(os.stat(file).st_mtime)
+ # mark exif made by jbrout
+ self.__info["Exif.Image.Make"] = "jBrout"
+ self.__info["Exif.Image.DateTime"] = fd
+ self.__info["Exif.Photo.DateTimeOriginal"] = fd
+ self.__info["Exif.Photo.DateTimeDigitized"] = fd
self.__info.writeMetadata()

#exifdate = self.__info["Exif.Image.DateTime"]
@@ -213,135 +229,161 @@ class PhotoCmd(object):
#-----------------------------------------------------------
# try to autorot, if wanted
#-----------------------------------------------------------
- if needAutoRotation :
+ if needAutoRotation:
self.transform("auto")

#-----------------------------------------------------------
# try to autorename, if wanted
#-----------------------------------------------------------
- if needAutoRename :
- folder=os.path.dirname(file)
+ if needAutoRename:
+ folder = os.path.dirname(file)
nameShouldBe = unicode(exifdate.strftime(PhotoCmd.format))
- newname = nameShouldBe+u'.'+file.split('.')[-1].lower()
+ newname = nameShouldBe + u'.' + file.split('.')[-1].lower()

- if not os.path.isfile(os.path.join(folder,newname)):
+ if not os.path.isfile(os.path.join(folder, newname)):
# there is no files which already have this name
# we can simply rename it
- newfile = os.path.join(folder,newname)
+ newfile = os.path.join(folder, newname)

- os.rename(file,newfile)
+ os.rename(file, newfile)
self.__file = newfile
else:
# there is a file, in the same folder which already got
# the same name

- if nameShouldBe != os.path.basename(file)[:len(nameShouldBe)]:
- while os.path.isfile(os.path.join(folder,newname) ):
- newname=PhotoCmd.giveMeANewName(newname)
+ if nameShouldBe != \
+ os.path.basename(file)[:len(nameShouldBe)]:
+ while os.path.isfile(os.path.join(folder, newname)):
+ newname = PhotoCmd.giveMeANewName(newname)

- newfile = os.path.join(folder,newname)
+ newfile = os.path.join(folder, newname)

- os.rename(file,newfile)
+ os.rename(file, newfile)
self.__file = newfile
- #self.debug( "*WARNING* File %s needs to be renamed -> %s" % (file,newfile) )
+ #self.debug("*WARNING* File %s to be renamed -> %s"
+ # % (file, newfile) )

self.__refresh()

def __refresh(self):
self.__info = pyexiv.Exiv2Metadata(
- pyexiv2.ImageMetadata(self.__file))
+ pyexiv2.ImageMetadata(self.__file))
self.__info.readMetadata()

if "Exif.Image.Make" in self.__info.exifKeys():
- self.__isreal = (self.__info["Exif.Image.Make"]!="jBrout") # except if a cam maker is named jBrout (currently, it doesn't exist ;-)
+ # except if a cam maker is named jBrout (currently, it
+ # doesn't exist ;-)
+ self.__isreal = (self.__info["Exif.Image.Make"] != "jBrout")
else:
# can only be here after a destroyInfo() or file has no exif data
self.__isreal = False

if "Exif.Photo.DateTimeOriginal" in self.__info.exifKeys():
- #self.__exifdate = self.__info["Exif.Image.DateTime"].strftime("%Y%m%d%H%M%S")
- self.__exifdate = self.__info["Exif.Photo.DateTimeOriginal"].strftime("%Y%m%d%H%M%S")
+ #self.__exifdate =
+ # self.__info["Exif.Image.DateTime"].strftime("%Y%m%d%H%M%S")
+ self.__exifdate = \
+ self.__info[
+ "Exif.Photo.DateTimeOriginal"].strftime("%Y%m%d%H%M%S")
elif "Exif.Image.DateTime" in self.__info.exifKeys():
- self.__exifdate = self.__info["Exif.Image.DateTime"].strftime("%Y%m%d%H%M%S")
+ self.__exifdate = \
+ self.__info["Exif.Image.DateTime"].strftime("%Y%m%d%H%M%S")
else:
- # can only be here after a destroyInfo() or if file has no exif info
- self.__exifdate=""
+ # can only be here after a destroyInfo() or if file has no
+ # exif info
+ self.__exifdate = ""

self.__filedate = self.__exifdate

try:
- w,h= Image.open(self.__file).size
+ w, h = Image.open(self.__file).size
except IOError:
- w,h=0,0 # XXX not recognized yetwith exiv2
- self.__resolution = "%d x %d" % (w,h) # REAL SIZE !
+ w, h = 0, 0 # XXX not recognized yetwith exiv2
+ self.__resolution = "%d x %d" % (w, h) # REAL SIZE !

if "Exif.Photo.Flash" in self.__info.exifKeys():
- v=self.__info.interpretedExifValue("Exif.Photo.Flash")
+ v = self.__info.interpretedExifValue("Exif.Photo.Flash")
if v:
- if v[:2].lower() in ["fi","ye"]: # fired, yes, ...
- self.__isflash = "Yes"
+ if v[:2].lower() in ["fi", "ye"]: # fired, yes, ...
+ self.__isflash = "Yes"
else:
- self.__isflash = "No"
+ self.__isflash = "No"
else:
- self.__isflash = ""
+ self.__isflash = ""
else:
- self.__isflash =""
+ self.__isflash = ""

self.__comment = decode(self.__info.getComment())

if "Xmp.xmp.Rating" in self.__info.xmpKeys():
# First, XMP
r = int(self.__info["Xmp.xmp.Rating"])
- if r<0: r=0
- elif r>5: r=5
+ if r < 0:
+ r = 0
+ elif r > 5:
+ r = 5
self.__rating = r
- if not "Exif.Image.RatingPercent" in self.__info.exifKeys() or not "Exif.Image.Rating" in self.__info.exifKeys() or self.__info["Exif.Image.Rating"] != r:
- self.__info["Exif.Image.Rating"]=self.__rating
- if r>=5: r=99
- elif r>1: r=(r-1)*25
- elif r<=0: r=0
- self.__info["Exif.Image.RatingPercent"]=r # short, but libexiv2 should convert this
+ if not "Exif.Image.RatingPercent" in self.__info.exifKeys() \
+ or not "Exif.Image.Rating" in self.__info.exifKeys() or \
+ self.__info["Exif.Image.Rating"] != r:
+ self.__info["Exif.Image.Rating"] = self.__rating
+ if r >= 5:
+ r = 99
+ elif r > 1:
+ r = (r - 1) * 25
+ elif r <= 0:
+ r = 0
+ # short, but libexiv2 should convert this
+ self.__info["Exif.Image.RatingPercent"] = r
self.__info.writeMetadata()
elif "Exif.Image.RatingPercent" in self.__info.exifKeys():
# Then EXIF, RatingPercent key first
r = int(self.__info["Exif.Image.RatingPercent"])
- if r>=99: r=5
- elif r>1: r=1+r/25
- elif r<=0: r=0
+ if r >= 99:
+ r = 5
+ elif r > 1:
+ r = 1 + r / 25
+ elif r <= 0:
+ r = 0
self.__rating = r
- if not "Xmp.xmp.Rating" in self.__info.xmpKeys() or self.__info["Xmp.xmp.Rating"] != self.__rating:
- self.__info["Xmp.xmp.Rating"]=self.__rating
+ if not "Xmp.xmp.Rating" in self.__info.xmpKeys() or \
+ self.__info["Xmp.xmp.Rating"] != self.__rating:
+ self.__info["Xmp.xmp.Rating"] = self.__rating
self.__info.writeMetadata()
elif "Exif.Image.Rating" in self.__info.exifKeys():
# Fallback to Rating if RatingPercent is not available
r = int(self.__info["Exif.Image.Rating"])
- if r<0: r=0
- elif r>5: r=5
+ if r < 0:
+ r = 0
+ elif r > 5:
+ r = 5
self.__rating = r
- if not "Xmp.xmp.Rating" in self.__info.xmpKeys() or self.__info["Xmp.xmp.Rating"] != self.__rating:
- self.__info["Xmp.xmp.Rating"]=self.__rating
+ if not "Xmp.xmp.Rating" in self.__info.xmpKeys() or \
+ self.__info["Xmp.xmp.Rating"] != self.__rating:
+ self.__info["Xmp.xmp.Rating"] = self.__rating
self.__info.writeMetadata()
else:
- self.__rating = None # dont touch if no rating tag was set before
+ # dont touch if no rating tag was set before
+ self.__rating = None

self.__tags = [decode(i) for i in self.__info.getTags()]

-
- def __saveTB(self,f): # not used
+ def __saveTB(self, f): # not used
self.__info.dumpThumbnailToFile(f)

def __getThumbnail(self):
try:
- t=self.__info.getThumbnailData()
+ t = self.__info.getThumbnailData()
return t[1]
- except IOError: #Cannot access image thumbnail
+ except IOError: # Cannot access image thumbnail
return ""

def showAll(self):
for key in self.__info.exifKeys():
- print key,(self.__info[key],) # tuple to avoid unicode error in print
+ # tuple to avoid unicode error in print
+ print key, (self.__info[key],)
for key in self.__info.iptcKeys():
- print key,(self.__info[key],) # tuple to avoid unicode error in print
+ # tuple to avoid unicode error in print
+ print key, (self.__info[key],)

def __repr__(self):
return """file : %s
@@ -351,52 +393,51 @@ resolution : %s
filedate : %s
exifdate : %s
thumb : %d
-isreal : %s""" % (
- self.__file,
+isreal : %s""" % (self.__file,
self.__readonly,
self.__isflash,
self.__resolution,
self.__filedate,
self.__exifdate,
len(self.__getThumbnail()),
- self.__isreal,
- )
+ self.__isreal,)

- def redate(self,w,d,h,m,s):
+ def redate(self, w, d, h, m, s):
"""
- redate jpeg file from offset : weeks, days, hours, minutes,seconds
+ redate jpeg file from offset : weeks, days, hours, minutes, seconds
"""

#TODO:attention au fichier sans EXIF ici !!!!

- #fd=self.__info["Exif.Image.DateTime"]
- fd=self.__info["Exif.Photo.DateTimeOriginal"]
- fd+=timedelta(weeks=w, days=d,hours=h,minutes=m,seconds=s)
+ #fd = self.__info["Exif.Image.DateTime"]
+ fd = self.__info["Exif.Photo.DateTimeOriginal"]
+ fd += timedelta(weeks=w, days=d, hours=h, minutes=m, seconds=s)
return self.setDate(fd)

- def setDate(self,fd):
+ def setDate(self, fd):
"""
set absolute date of jpeg file.
"""
- if self.__readonly: return False
- self.__info["Exif.Image.DateTime"]=fd
- self.__info["Exif.Photo.DateTimeOriginal"]=fd
- self.__info["Exif.Photo.DateTimeDigitized"]=fd
+ if self.__readonly:
+ return False
+ self.__info["Exif.Image.DateTime"] = fd
+ self.__info["Exif.Photo.DateTimeOriginal"] = fd
+ self.__info["Exif.Photo.DateTimeDigitized"] = fd

self.__maj()
return True

-
def clear(self):
- if self.__readonly: return False
+ if self.__readonly:
+ return False
self.__info.clearTags()
self.__maj()
return True

-
- def sub(self,t):
- assert type(t)==unicode
- if self.__readonly: return False
+ def sub(self, t):
+ assert type(t) == unicode
+ if self.__readonly:
+ return False
if t in self.__tags:
self.__tags.remove(t)
self.__majTags()
@@ -404,9 +445,10 @@ isreal : %s""" % (
else:
return False

- def add(self,t):
- assert type(t)==unicode
- if self.__readonly: return False
+ def add(self, t):
+ assert type(t) == unicode
+ if self.__readonly:
+ return False
if t in self.__tags:
return False
else:
@@ -414,12 +456,13 @@ isreal : %s""" % (
self.__majTags()
return True

- def addTags(self,tags): # *new*
+ def addTags(self, tags): # *new*
""" add a list of tags to the file, return False if it can't """
- if self.__readonly: return False
+ if self.__readonly:
+ return False
isModified = False
for t in tags:
- assert type(t)==unicode
+ assert type(t) == unicode
if t not in self.__tags:
isModified = True
self.__tags.append(t)
@@ -428,14 +471,14 @@ isreal : %s""" % (
self.__majTags()
return True

-
- def subTags(self,tags): # *new*
+ def subTags(self, tags): # *new*
""" sub a list of tags to the file, return False if it can't """
- if self.__readonly: return False
+ if self.__readonly:
+ return False

isModified = False
for t in tags:
- assert type(t)==unicode
+ assert type(t) == unicode
if t in self.__tags:
isModified = True
self.__tags.remove(t)
@@ -447,14 +490,16 @@ isreal : %s""" % (
def destroyInfo(self):
""" destroy ALL info (exif/iptc)
"""
- if self.__readonly: return False
+ if self.__readonly:
+ return False

# delete EXIF and IPTC tags :
- l=self.__info.exifKeys() + self.__info.iptcKeys() + self.__info.xmpKeys()
+ l = self.__info.exifKeys() + \
+ self.__info.iptcKeys() + self.__info.xmpKeys()
for i in l:
try:
del self.__info[i]
- except KeyError: # 'tag not set'
+ except KeyError: # 'tag not set'
# the tag seems not to be here, so
# we don't need to clear it, no ?
pass
@@ -462,14 +507,13 @@ isreal : %s""" % (
self.__info.deleteThumbnail() # seems not needed !
self.__info.clearComment()

- self.__maj() # so, ONLY CASE where self.exifdate==""
+ self.__maj() # so, ONLY CASE where self.exifdate == ""
return True

-
- def copyInfoTo(self,file2):
+ def copyInfoTo(self, file2):
""" copy exif/iptc to "file2", return dest photonode
"""
- assert type(file2)==unicode
+ assert type(file2) == unicode
assert os.path.isfile(file2)

self.__info.copyToFile(file2)
@@ -477,19 +521,20 @@ isreal : %s""" % (
return PhotoCmd(file2)

def rebuildExifTB(self):
- if self.__readonly: return False
+ if self.__readonly:
+ return False

try:
- im= Image.open(self.__file)
- im.thumbnail((160,160), Image.ANTIALIAS)
- except Exception,m:
- print "*WARNING* can't load this file : ",(self.__file,),m
- im=None
+ im = Image.open(self.__file)
+ im.thumbnail((160, 160), Image.ANTIALIAS)
+ except Exception, m:
+ print "*WARNING* can't load this file : ", (self.__file, ), m
+ im = None

if im:
file1 = StringIO.StringIO()
im.save(file1, "JPEG")
- buf=file1.getvalue()
+ buf = file1.getvalue()
file1.close()
self.__info.setThumbnailData(buf)

@@ -510,11 +555,11 @@ isreal : %s""" % (
pass
self.__refresh()

- def addComment(self,c):
- # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
- assert type(c)==unicode
- c=c.strip()
- if c=="":
+ def addComment(self, c):
+ # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
+ assert type(c) == unicode
+ c = c.strip()
+ if c == "":
self.__info.clearComment()
else:
self.__info.setComment(c.encode("utf_8"))
@@ -522,22 +567,31 @@ isreal : %s""" % (
self.__maj()
return True

- def addRating(self,r):
- # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
- assert type(r)==int
- self.__info["Exif.Image.Rating"]=r # short, but libexiv2 should convert this
- self.__info["Xmp.xmp.Rating"]=r # short, but libexiv2 should convert this
- if r>=5: r=99
- elif r>1: r=(r-1)*25
- elif r<=0: r=0
- self.__info["Exif.Image.RatingPercent"]=r # short, but libexiv2 should convert this
- self.__maj() # save it
+ def addRating(self, r):
+ # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
+ assert type(r) == int
+ # short, but libexiv2 should convert this
+ self.__info["Exif.Image.Rating"] = r
+ # short, but libexiv2 should convert this
+ self.__info["Xmp.xmp.Rating"] = r
+ if r >= 5:
+ r = 99
+ elif r > 1:
+ r = (r - 1) * 25
+ elif r <= 0:
+ r = 0
+ # short, but libexiv2 should convert this
+ self.__info["Exif.Image.RatingPercent"] = r
+ self.__maj() # save it
return True

- def rotate(self,sens):
- # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
- """ rotate LOSSLESS the picture 'file', and its internal thumbnail according 'sens' (R/L)"""
- if sens=="R":
+ def rotate(self, sens):
+ # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
+ """
+ rotate LOSSLESS the picture 'file', and its internal
+ thumbnail according 'sens' (R/L)
+ """
+ if sens == "R":
deg = "90"
opt = "-9"
else:
@@ -545,20 +599,23 @@ isreal : %s""" % (
opt = "-2"

if _Command.isWin:
- ret= _Command._run( [_Command._jpegtran,'-rotate',deg,'-copy','all',self.__file,self.__file] )
+ _Command._run([_Command._jpegtran,
+ '-rotate', deg, '-copy', 'all',
+ self.__file, self.__file])
# rebuild the exif thumb, because jpegtran doesn't do it on windows
self.rebuildExifTB()
else:
- ret= _Command._run( [_Command._exiftran,opt,'-i',self.__file] ) # exiftran rotate internal exif thumb
+ # exiftran rotate internal exif thumb
+ _Command._run([_Command._exiftran, opt, '-i', self.__file])

self.__refresh()

- def transform(self,sens):
- # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
+ def transform(self, sens):
+ # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
""" LOSSLESS transformation of the picture 'file', and its internal
thumbnail according 'sens'
"""
- if sens=="auto":
+ if sens == "auto":
if 'Exif.Image.Orientation' in self.__info.exifKeys():
exifSens = int(self.__info['Exif.Image.Orientation'])
if exifSens not in autoTrans.keys():
@@ -566,45 +623,47 @@ isreal : %s""" % (
sens = autoTrans[exifSens][0]
else:
sens = autoTrans[1][0]
- if sens=="rotate90":
+ if sens == "rotate90":
jpegtranOpt = ["-rotate", "90"]
exiftranOpt = "-9"
- elif sens=="rotate180":
+ elif sens == "rotate180":
jpegtranOpt = ["-rotate", "180"]
exiftranOpt = "-1"
- elif sens=="rotate270":
+ elif sens == "rotate270":
jpegtranOpt = ["-rotate", "270"]
exiftranOpt = "-2"
- elif sens=="flipHorizontal":
+ elif sens == "flipHorizontal":
jpegtranOpt = ["-flip", "horizontal"]
exiftranOpt = "-F"
- elif sens=="flipVertical":
+ elif sens == "flipVertical":
jpegtranOpt = ["-flip", "vertical"]
exiftranOpt = "-f"
- elif sens=="transpose":
+ elif sens == "transpose":
jpegtranOpt = ["-transpose"]
exiftranOpt = "-t"
- elif sens=="transverse":
+ elif sens == "transverse":
jpegtranOpt = ["-transverse"]
exiftranOpt = "-T"

if not(sens == "none"):
if _Command.isWin:
- ret= _Command._run( [_Command._jpegtran]+jpegtranOpt+['-copy','all',self.__file,self.__file] )
- # rebuild the exif thumb and reset the orientation tag,
- # because jpegtran doesn't do it on windows
- self.rebuildExifTB()
- self.__info['Exif.Image.Orientation']=1
- self.__maj()
+ _Command._run([_Command._jpegtran] + jpegtranOpt +
+ ['-copy', 'all', self.__file, self.__file])
+ # rebuild the exif thumb and reset the orientation tag,
+ # because jpegtran doesn't do it on windows
+ self.rebuildExifTB()
+ self.__info['Exif.Image.Orientation'] = 1
+ self.__maj()
else:
- ret= _Command._run( [_Command._exiftran,exiftranOpt,'-i',self.__file] ) # exiftran rotate internal exif thumb
+ # exiftran rotate internal exif thumb
+ _Command._run([_Command._exiftran, exiftranOpt,
+ '-i', self.__file])

self.__refresh()

-
#~ def rotates(self): # NO ROTATE LOSS LESS ;-(
- #~ im=Image.open(self.__file)
- #~ im=im.transpose(Image.ROTATE_90)
+ #~ im = Image.open(self.__file)
+ #~ im = im.transpose(Image.ROTATE_90)
#~ im.save(self.__file)

def isThumbOk(self):
@@ -613,28 +672,29 @@ isreal : %s""" % (
# -1 : no thumb
isThumbOk = None

-
- im=Image.open(self.__file)
+ im = Image.open(self.__file)
im.verify()
- w,h = im.size
- isImageHorizon =w>h
+ w, h = im.size
+ isImageHorizon = w > h

- t=self.__getThumbnail()
+ t = self.__getThumbnail()
if t:
- f=StringIO.StringIO()
+ f = StringIO.StringIO()
f.write(t)
f.seek(0)
- tw,th=Image.open(f).size
- isTImageHorizon = tw>th
+ tw, th = Image.open(f).size
+ isTImageHorizon = tw > th

if isImageHorizon == isTImageHorizon:
isThumbOk = 1
else:
isThumbOk = 0
else:
- isThumbOk=-1
+ isThumbOk = -1

- assert (self.__info["Exif.Image.DateTime"]==self.__info["Exif.Photo.DateTimeOriginal"]==self.__info["Exif.Photo.DateTimeDigitized"])
+ assert (self.__info["Exif.Image.DateTime"] ==
+ self.__info["Exif.Photo.DateTimeOriginal"] ==
+ self.__info["Exif.Photo.DateTimeDigitized"])

return isThumbOk

@@ -643,58 +703,58 @@ isreal : %s""" % (
# """
# normalize name (only real exif pictures !!!!)
# """
- # assert type(file)==unicode
- # p=PhotoCmd(file)
+ # assert type(file) == unicode
+ # p = PhotoCmd(file)
# p.__rename()
# return p.file

@staticmethod
def setNormalizeNameFormat(format):
- PhotoCmd.format=format
-
+ PhotoCmd.format = format

@staticmethod
def giveMeANewName(name):
- n,ext = os.path.splitext(name)
- mo= re.match("(.*)\((\d+)\)$",n)
+ n, ext = os.path.splitext(name)
+ mo = re.match("(.*)\((\d+)\)$", n)
if mo:
- n=mo.group(1)
- num=int(mo.group(2)) +1
+ n = mo.group(1)
+ num = int(mo.group(2)) + 1
else:
- num=1
+ num = 1

- return u"%s(%d)%s" % (n,num,ext)
+ return u"%s(%d)%s" % (n, num, ext)


#@staticmethod
- #def prepareFile(file,needRename,needAutoRot):
+ #def prepareFile(file, needRename, needAutoRot):
# """
# prepare file, rotating/autorotating according exif tags
# (same things as normalizename + autorot, in one action)
# only called at IMPORT/REFRESH albums
# """
- # assert type(file)==unicode
+ # assert type(file) == unicode
#
#
# if needAutoRot:
# if _Command.isWin:
# # do nothing
- # # -> because no gpl tools which rotate well (img+thumb) automatically according exif
+ # # -> because no gpl tools which rotate well (img+thumb)
+ # # automatically according exif
# # if you provide me one, i'll integrate here
# pass
# else:
- # _Command._run( [_Command._exiftran,'-ai',file] )
+ # _Command._run( [_Command._exiftran, '-ai', file] )
#
# if needRename:
# return PhotoCmd.normalizeName(file)
# else:
# return file

-if __name__=="__main__":
+if __name__ == "__main__":

- #~ f=u"images_exemples/IMG_3320.JPG"
+ #~ f = u"images_exemples/IMG_3320.JPG"
#~ help(pyexiv2)
- #~ i=PhotoCmd(f)
+ #~ i = PhotoCmd(f)
#~ i.showAll()
#~ i.showExiv()
#~ print i.file
--
1.8.3.1

Reply all
Reply to author
Forward
0 new messages