Exemple #1
0
def update_iptcdata(filepath,
                    new_caption,
                    new_keywords,
                    new_datetime,
                    new_rating,
                    new_gps,
                    new_rectangles,
                    new_persons,
                    image_width=-1,
                    image_height=-1,
                    hierarchical_subject=None,
                    preserve_time=True,
                    title=""):
    """Updates the caption and keywords of an image file."""
    # Some cameras write into Description, so we wipe it out to not cause
    # conflicts with Caption-Abstract.
    command = [EXIFTOOL, '-F', '-m', '-Description=']
    if preserve_time:
        command.append("-P")

    # marcelb: added title support
    if title:
        command.append('-Headline=%s' % (title, ))

    tmp = _write_caption_file(new_caption, command)
    if new_datetime:
        try:
            command.append('-DateTimeOriginal="%s"' %
                           (new_datetime.strftime("%Y:%m:%d %H:%M:%S")))
        except ValueError, ex:
            su.perr("Cannot update timestamp for %s: %s" % (filepath, str(ex)))
Exemple #2
0
    def generate(self, options):
        """makes sure all files exist in other album, and generates if
           necessary."""
        try:
            source_file = su.resolve_alias(self.photo.image_path)
            do_export = self._check_need_to_export(source_file, options)

            # if we use links, we update the IPTC data in the original file
            do_iptc = (options.iptc == 1 and do_export) or options.iptc == 2
            if do_iptc and options.link:
                if self.check_iptc_data(source_file, options, file_updated=do_export):
                    do_export = True

            exists = True  # True if the file exists or was updated.
            if do_export:
                exists = imageutils.copy_or_link_file(source_file,
                                                      self.export_file,
                                                      options.dryrun,
                                                      options.link,
                                                      self.size,
                                                      options)
            else:
                _logger.debug(u'%s up to date.', self.export_file)

            # if we copy, we update the IPTC data in the copied file
            if exists and do_iptc and not options.link:
                self.check_iptc_data(self.export_file, options, file_updated=do_export)

            if (options.originals and self.photo.originalpath and
                not self.photo.rotation_is_only_edit):
                self._generate_original(options)
        except (OSError, MacOS.Error) as ose:
            su.perr(u"Failed to export %s to %s: %s" % (self.photo.image_path, self.export_file,
                                                        ose))
def _get_iptc_cache(image_file):
    """Gets the _IptcCache for the folder of image_file."""
    folder = os.path.split(image_file)[0]
    if _get_iptc_cache.cache and _get_iptc_cache.cache.folder == folder:
        return _get_iptc_cache.cache
    
    # We have no cached data, or there are for the wrong folder.
    _get_iptc_cache.cache = None
    save_path = os.path.join(folder, CACHE_NAME)
    if os.path.exists(save_path):
        cache_file = None
        try:
            cache_file = open(save_path)
            _get_iptc_cache.cache = cPickle.load(cache_file)
        except Exception, ex:
            su.perr(u"Could not read Phoshare IPTC cache data from %s: %s." % (
                save_path, unicode(ex)))

        if cache_file:
            cache_file.close()

        # Make sure the folder matches.
        if _get_iptc_cache.cache and _get_iptc_cache.cache.folder != folder:
            _get_iptc_cache.cache = None

        try:
            if _get_iptc_cache.cache and _get_iptc_cache.cache.cache_version != CACHE_VERSION:
                _get_iptc_cache.cache = None
        except AttributeError:
            su.perr(u"Found non-cache data in %s, ignoring" % (folder))
            _get_iptc_cache.cache = None

        if _get_iptc_cache.cache:
            _get_iptc_cache.cache.clear_expired()
Exemple #4
0
def _get_iptc_cache(image_file):
    """Gets the _IptcCache for the folder of image_file."""
    folder = os.path.split(image_file)[0]
    if _get_iptc_cache.cache and _get_iptc_cache.cache.folder == folder:
        return _get_iptc_cache.cache

    # We have no cached data, or there are for the wrong folder.
    _get_iptc_cache.cache = None
    save_path = os.path.join(folder, CACHE_NAME)
    if os.path.exists(save_path):
        cache_file = None
        try:
            cache_file = open(save_path)
            _get_iptc_cache.cache = cPickle.load(cache_file)
        except Exception, ex:
            su.perr(u"Could not read Phoshare IPTC cache data from %s: %s." %
                    (save_path, unicode(ex)))

        if cache_file:
            cache_file.close()

        # Make sure the folder matches.
        if _get_iptc_cache.cache and _get_iptc_cache.cache.folder != folder:
            _get_iptc_cache.cache = None

        try:
            if _get_iptc_cache.cache and _get_iptc_cache.cache.cache_version != CACHE_VERSION:
                _get_iptc_cache.cache = None
        except AttributeError:
            su.perr(u"Found non-cache data in %s, ignoring" % (folder))
            _get_iptc_cache.cache = None

        if _get_iptc_cache.cache:
            _get_iptc_cache.cache.clear_expired()
Exemple #5
0
    def generate(self, options):
        """makes sure all files exist in other album, and generates if
           necessary."""
        source_file = self.photo.image_path
        try:
            do_export = self._check_need_to_export(source_file, options)

            # if we use links, we update the IPTC data in the original file
            do_iptc = (options.iptc == 1 and do_export) or options.iptc == 2
            if do_iptc and options.link:
                if self.check_iptc_data(source_file, options):
                    do_export = True

            exists = True  # True if the file exists or was updated.
            if do_export:
                exists = imageutils.copy_or_link_file(
                    source_file, self.export_file, options.dryrun,
                    options.link, options.size, options.update)
            else:
                _logger.debug(u'%s up to date.', self.export_file)

            # if we copy, we update the IPTC data in the copied file
            if exists and do_iptc and not options.link:
                self.check_iptc_data(self.export_file, options)

            if (options.originals and self.photo.originalpath
                    and not self.photo.rotation_is_only_edit):
                self._generate_original(options)
        except (OSError, MacOS.Error) as ose:
            su.perr("Failed to export %s: %s" % (source_file, ose))
Exemple #6
0
def _parse_datetime_original(xml_desc, iptc_data, image_file):
    """Parses the DateTimeOriginal data from exiftool output."""
    for xml_element in xml_desc.getElementsByTagName(
            'ExifIFD:DateTimeOriginal'):
        if not xml_element.firstChild:
            continue
        try:
            date_time_original = time.strptime(
                xml_element.firstChild.nodeValue, '%Y:%m:%d %H:%M:%S')
            iptc_data.date_time_original = datetime.datetime(
                date_time_original.tm_year, date_time_original.tm_mon,
                date_time_original.tm_mday, date_time_original.tm_hour,
                date_time_original.tm_min, date_time_original.tm_sec)
        except ValueError, _ve:
            su.perr('Exiftool returned an invalid date %s for %s - ignoring.' %
                    (xml_element.firstChild.nodeValue, image_file))
Exemple #7
0
def update_iptcdata(filepath, new_caption, new_keywords, new_datetime,
                    new_rating, new_gps, new_rectangles, new_persons,
                    image_width=-1, image_height=-1, hierarchical_subject=None,
                    preserve_time=True):
    """Updates the caption and keywords of an image file."""
    # Some cameras write into Description, so we wipe it out to not cause
    # conflicts with Caption-Abstract.
    command = [EXIFTOOL, '-F', '-m', '-Description=']
    if preserve_time:
        command.append("-P")
    tmp = _write_caption_file(new_caption, command)
    if new_datetime:
        try:
            command.append('-DateTimeOriginal="%s"' % (
                new_datetime.strftime("%Y:%m:%d %H:%M:%S")))
        except ValueError, ex:
            su.perr("Cannot update timestamp for %s: %s" % (filepath, str(ex)))
def _parse_datetime_original(xml_desc, iptc_data, image_file):
    """Parses the DateTimeOriginal data from exiftool output."""
    for xml_element in xml_desc.getElementsByTagName('ExifIFD:DateTimeOriginal'):
        if not xml_element.firstChild:
            continue
        try:
            date_time_original = time.strptime(
                xml_element.firstChild.nodeValue, '%Y:%m:%d %H:%M:%S')
            iptc_data.date_time_original = datetime.datetime(
                date_time_original.tm_year,
                date_time_original.tm_mon,
                date_time_original.tm_mday,
                date_time_original.tm_hour,
                date_time_original.tm_min,
                date_time_original.tm_sec)
        except ValueError, _ve:
            su.perr('Exiftool returned an invalid date %s for %s - ignoring.' % (
                xml_element.firstChild.nodeValue, image_file))
Exemple #9
0
def get_iptc_data(image_file):
    """get caption, keywords, datetime, rating, and GPS info all in one 
       operation."""
    output = su.execandcombine(
        (EXIFTOOL, "-X", "-m", "-q", "-q", '-c', '%.6f', "-Keywords", 
         "-Caption-Abstract", "-DateTimeOriginal", "-Rating", "-GPSLatitude",
         "-Subject", "-GPSLongitude", "-RegionRectangle",
         "-RegionPersonDisplayName", image_file))
  
    keywords = []
    caption = None
    date_time_original = None
    rating = 0
    gps = None
    region_names = []
    region_rectangles = []
    if output:
        try:
            gps_latitude = None
            gps_longitude = None

            xml_data = minidom.parseString(output)

            for xml_desc in xml_data.getElementsByTagName('rdf:Description'):
                _get_xml_nodevalues(xml_desc, 'IPTC:Keywords', keywords)
                # Keywords can also be stored as Subject in the XMP directory
                _get_xml_nodevalues(xml_desc, 'XMP:Subject', keywords)
                for xml_caption in xml_data.getElementsByTagName(
                    'IPTC:Caption-Abstract'):
                    caption = xml_caption.firstChild.nodeValue
                for xml_element in xml_data.getElementsByTagName(
                    'ExifIFD:DateTimeOriginal'):
                    if not xml_element.firstChild:
                        continue
                    try:
                        date_time_original = time.strptime(
                            xml_element.firstChild.nodeValue,
                            '%Y:%m:%d %H:%M:%S')
                        date_time_original = datetime.datetime(
                            date_time_original.tm_year,
                            date_time_original.tm_mon,
                            date_time_original.tm_mday,
                            date_time_original.tm_hour,
                            date_time_original.tm_min,
                            date_time_original.tm_sec)
                    except ValueError, _ve:
                        su.perr('Exiftool returned an invalid '
                                'date %s for %s - ignoring.' % (
                            xml_element.firstChild.nodeValue,
                            image_file))
                for xml_element in xml_data.getElementsByTagName(
                    'XMP-xmp:Rating'):
                    rating = int(xml_element.firstChild.nodeValue)
                for xml_element in xml_data.getElementsByTagName(
                    'Composite:GPSLatitude'):
                    gps_latitude = xml_element.firstChild.nodeValue
                for xml_element in xml_data.getElementsByTagName(
                    'Composite:GPSLongitude'):
                    gps_longitude = xml_element.firstChild.nodeValue
                string_rectangles = []
                _get_xml_nodevalues(xml_desc, 'XMP-MP:RegionRectangle', 
                                    string_rectangles)
                for string_rectangle in string_rectangles:
                    rectangle = []
                    for c in string_rectangle.split(','):
                        rectangle.append(float(c))
                    region_rectangles.append(rectangle)
                _get_xml_nodevalues(xml_desc, 'XMP-MP:RegionPersonDisplayName', 
                                    region_names)

            xml_data.unlink()
            if gps_latitude and gps_longitude:
                gps = imageutils.GpsLocation().from_composite(gps_latitude, 
                                                              gps_longitude)
        except parsers.expat.ExpatError, ex:
            su.perr('Could not parse exiftool output %s: %s' % (
                output, ex))
Exemple #10
0
def run_phoshare(cmd_args):
    """main routine for phoshare."""
    parser = get_option_parser()
    (options, args) = parser.parse_args(cmd_args)
    if len(args) != 0:
        parser.error("Found some unrecognized arguments on the command line.")

    if options.version:
        print '%s %s' % (phoshare.phoshare_version.PHOSHARE_VERSION,
                         phoshare.phoshare_version.PHOSHARE_BUILD)
        return 1

    if options.iptc > 0 and not exiftool.check_exif_tool():
        print >> sys.stderr, ("Exiftool is needed for the --itpc or --iptcall" +
          " options.")
        return 1

    if options.size and options.link:
        parser.error("Cannot use --size and --link together.")

    if not options.iphoto:
        parser.error("Need to specify the iPhoto library with the --iphoto "
                     "option.")

    if options.export or options.picasaweb or options.checkalbumsize:
        if not (options.albums or options.events or options.smarts or
                options.facealbums):
            parser.error("Need to specify at least one event, album, or smart "
                         "album for exporting, using the -e, -a, or -s "
                         "options.")
    else:
        parser.error("No action specified. Use --export to export from your "
                     "iPhoto library.")

    if options.picasaweb:
        if options.picasapassword:
            google_password = options.picasapassword
        else:
            google_password = getpass.getpass('Google password for %s: ' %
                                              options.picasaweb)

    if options.ratings:
        options.ratings = [int(r) for r in options.ratings.split(",")]

    if options.reverse:
        if not options.dryrun:
            su.pout(u"Turning on dryrun mode because of --reverse option.")
        options.dryrun = True

    logging_handler = logging.StreamHandler()
    logging_handler.setLevel(logging.DEBUG if options.verbose else logging.INFO)
    _logger.addHandler(logging_handler)

    album_xml_file = iphotodata.get_album_xmlfile(
        su.expand_home_folder(options.iphoto))
    if options.omitdatabasefile:
        album_sql_file=""
    else:
        album_sql_file = iphotodata.get_album_sqlfile(
            su.expand_home_folder(options.iphoto))
            
    data = iphotodata.get_iphoto_data(album_xml_file, album_sql_file, ratings=options.ratings,
                                       verbose=options.verbose, aperture=options.aperture)
    if options.originals and options.export:
        data.load_aperture_originals()
        
    options.aperture = data.aperture and not data.aperture_data
    options.foldertemplate = unicode(options.foldertemplate)
    options.nametemplate = unicode(options.nametemplate)
    options.captiontemplate = unicode(options.captiontemplate)

    if options.checkalbumsize:
        data.checkalbumsizes(int(options.checkalbumsize))

    if options.export:
        album = ExportLibrary(su.expand_home_folder(options.export))
        export_iphoto(album, data, options.exclude, options)
    if options.picasaweb:
        try:
            import phoshare.picasaweb as picasaweb
            albums = picasaweb.PicasaAlbums(options.picasaweb, google_password)
            export_iphoto(albums, data, options.exclude, options)
        except ImportError:
            su.perr('Sorry, this version of Phoshare does not support uploading to PicasaWeb.')
def _get_iptc_data_exiftool(image_file):
    """Returns IptcData for an image file using exiftool."""
    args = [EXIFTOOL, "-X", "-m", "-q", "-q", '-c', '%.6f', "-Keywords", "-Caption-Abstract",
            "-ImageDescription", "-DateTimeOriginal", "-Rating", "-GPSLatitude",
            "-Subject", "-GPSLongitude", "-RegionName", "-RegionType",
            "-RegionAreaX", "-RegionAreaY", "-RegionAreaW", "-RegionAreaH",
            "-ImageWidth", "-ImageHeight", "-HierarchicalSubject", "-AlreadyApplied", image_file ]
    output = su.execandcombine(args)
    if not output:
        return None

    iptc_data = None
    try:
        xml_data = minidom.parseString(output)
        for xml_desc in xml_data.getElementsByTagName('rdf:Description'):
            iptc_data = IptcData()
            iptc_data.rating = 0
            iptc_data.image_file = xml_desc.getAttribute("rdf:about")
            
            iptc_data.keywords = []
            _get_xml_nodevalues(xml_desc, 'IPTC:Keywords', iptc_data.keywords)
            _get_xml_nodevalues(xml_desc, 'XMP-lr:HierarchicalSubject',
                                iptc_data.hierarchical_subject)
            # Keywords can also be stored as Subject in the XMP directory
            _get_xml_nodevalues(xml_desc, 'XMP:Subject', iptc_data.keywords)
            for xml_caption in xml_desc.getElementsByTagName('IPTC:Caption-Abstract'):
                if xml_caption.firstChild:
                    iptc_data.caption = xml_caption.firstChild.nodeValue
            if not iptc_data.caption:
                for xml_caption in xml_desc.getElementsByTagName('IFD0:ImageDescription'):
                    if xml_caption.firstChild:
                        iptc_data.caption = xml_caption.firstChild.nodeValue
            _parse_datetime_original(xml_desc, iptc_data, image_file)
            for xml_element in xml_desc.getElementsByTagName('XMP-xmp:Rating'):
                if xml_element.firstChild:
                    iptc_data.rating = int(xml_element.firstChild.nodeValue)
            _parse_gps(xml_desc, iptc_data)
                
            for xml_element in xml_desc.getElementsByTagName('File:ImageWidth'):
                if xml_element.firstChild:
                    iptc_data.image_width = int(xml_element.firstChild.nodeValue)
            for xml_element in xml_desc.getElementsByTagName('File:ImageHeight'):
                if xml_element.firstChild:
                    iptc_data.image_height = int(xml_element.firstChild.nodeValue)
            for xml_element in xml_desc.getElementsByTagName('XMP-crs:AlreadyApplied'):
                if xml_element.firstChild:
                    iptc_data.already_applied = xml_element.firstChild.nodeValue

            #string_rectangles = []
            #_get_xml_nodevalues(xml_desc, 'XMP-MP:RegionRectangle', string_rectangles)
            #for string_rectangle in string_rectangles:
            #    rectangle = []
            #    for c in string_rectangle.split(','):
            #        rectangle.append(float(c))
            #    iptc_data.region_rectangles.append(rectangle)
            #_get_xml_nodevalues(xml_desc, 'XMP-MP:RegionPersonDisplayName', 
            #                    iptc_data.region_names)

            # Handle Region tags
            _parse_regions(xml_desc, iptc_data)
            break

        xml_data.unlink()
      
    except parsers.expat.ExpatError, ex:
        su.perr('Could not parse exiftool output %s: %s' % (output, ex))
Exemple #12
0
def _get_iptc_data_exiftool(image_file):
    """Returns IptcData for an image file using exiftool."""
    args = [
        EXIFTOOL, "-X", "-m", "-q", "-q", '-c', '%.6f', "-Keywords",
        "-Caption-Abstract", "-ImageDescription", "-DateTimeOriginal",
        "-Rating", "-GPSLatitude", "-Subject", "-GPSLongitude", "-RegionName",
        "-RegionType", "-RegionAreaX", "-RegionAreaY", "-RegionAreaW",
        "-RegionAreaH", "-ImageWidth", "-ImageHeight", "-HierarchicalSubject",
        "-AlreadyApplied", image_file
    ]
    output = su.execandcombine(args)
    if not output:
        return None

    iptc_data = None
    try:
        xml_data = minidom.parseString(output)
        for xml_desc in xml_data.getElementsByTagName('rdf:Description'):
            iptc_data = IptcData()
            iptc_data.rating = 0
            iptc_data.image_file = xml_desc.getAttribute("rdf:about")

            iptc_data.keywords = []
            _get_xml_nodevalues(xml_desc, 'IPTC:Keywords', iptc_data.keywords)
            _get_xml_nodevalues(xml_desc, 'XMP-lr:HierarchicalSubject',
                                iptc_data.hierarchical_subject)
            # Keywords can also be stored as Subject in the XMP directory
            _get_xml_nodevalues(xml_desc, 'XMP:Subject', iptc_data.keywords)
            for xml_caption in xml_desc.getElementsByTagName(
                    'IPTC:Caption-Abstract'):
                if xml_caption.firstChild:
                    iptc_data.caption = xml_caption.firstChild.nodeValue
            if not iptc_data.caption:
                for xml_caption in xml_desc.getElementsByTagName(
                        'IFD0:ImageDescription'):
                    if xml_caption.firstChild:
                        iptc_data.caption = xml_caption.firstChild.nodeValue
            _parse_datetime_original(xml_desc, iptc_data, image_file)
            for xml_element in xml_desc.getElementsByTagName('XMP-xmp:Rating'):
                if xml_element.firstChild:
                    iptc_data.rating = int(xml_element.firstChild.nodeValue)
            _parse_gps(xml_desc, iptc_data)

            for xml_element in xml_desc.getElementsByTagName(
                    'File:ImageWidth'):
                if xml_element.firstChild:
                    iptc_data.image_width = int(
                        xml_element.firstChild.nodeValue)
            for xml_element in xml_desc.getElementsByTagName(
                    'File:ImageHeight'):
                if xml_element.firstChild:
                    iptc_data.image_height = int(
                        xml_element.firstChild.nodeValue)
            for xml_element in xml_desc.getElementsByTagName(
                    'XMP-crs:AlreadyApplied'):
                if xml_element.firstChild:
                    iptc_data.already_applied = xml_element.firstChild.nodeValue

            #string_rectangles = []
            #_get_xml_nodevalues(xml_desc, 'XMP-MP:RegionRectangle', string_rectangles)
            #for string_rectangle in string_rectangles:
            #    rectangle = []
            #    for c in string_rectangle.split(','):
            #        rectangle.append(float(c))
            #    iptc_data.region_rectangles.append(rectangle)
            #_get_xml_nodevalues(xml_desc, 'XMP-MP:RegionPersonDisplayName',
            #                    iptc_data.region_names)

            # Handle Region tags
            _parse_regions(xml_desc, iptc_data)
            break

        xml_data.unlink()

    except parsers.expat.ExpatError, ex:
        su.perr('Could not parse exiftool output %s: %s' % (output, ex))
def run_phoshare(cmd_args):
    """main routine for phoshare."""
    parser = get_option_parser()
    (options, args) = parser.parse_args(cmd_args)
    if len(args) != 0:
        parser.error("Found some unrecognized arguments on the command line.")

    if options.version:
        print '%s %s' % (phoshare.phoshare_version.PHOSHARE_VERSION,
                         phoshare.phoshare_version.PHOSHARE_BUILD)
        return 1

    if options.iptc > 0 and not exiftool.check_exif_tool():
        print >> sys.stderr, ("Exiftool is needed for the --itpc or --iptcall" +
          " options.")
        return 1

    if options.size and options.link:
        parser.error("Cannot use --size and --link together.")

    if not options.iphoto:
        parser.error("Need to specify the iPhoto library with the --iphoto "
                     "option.")

    if options.export or options.picasaweb or options.checkalbumsize:
        if not (options.albums or options.events or options.smarts or
                options.facealbums):
            parser.error("Need to specify at least one event, album, or smart "
                         "album for exporting, using the -e, -a, or -s "
                         "options.")
    else:
        parser.error("No action specified. Use --export to export from your "
                     "iPhoto library.")

    if options.picasaweb:
        if options.picasapassword:
            google_password = options.picasapassword
        else:
            google_password = getpass.getpass('Google password for %s: ' %
                                              options.picasaweb)

    if options.ratings:
        options.ratings = [int(r) for r in options.ratings.split(",")]

    if options.reverse:
        if not options.dryrun:
            su.pout(u"Turning on dryrun mode because of --reverse option.")
        options.dryrun = True

    logging_handler = logging.StreamHandler()
    logging_handler.setLevel(logging.DEBUG if options.verbose else logging.INFO)
    _logger.addHandler(logging_handler)

    album_xml_file = iphotodata.get_album_xmlfile(
        su.expand_home_folder(options.iphoto))
    album_sql_file = iphotodata.get_album_sqlfile(
        su.expand_home_folder(options.iphoto))
    data = iphotodata.get_iphoto_data(album_xml_file, album_sql_file, ratings=options.ratings,
                                       verbose=options.verbose, aperture=options.aperture)
    if options.originals and options.export:
        data.load_aperture_originals()
        
    options.aperture = data.aperture and not data.aperture_data
    options.foldertemplate = unicode(options.foldertemplate)
    options.nametemplate = unicode(options.nametemplate)
    options.captiontemplate = unicode(options.captiontemplate)

    if options.checkalbumsize:
        data.checkalbumsizes(int(options.checkalbumsize))

    if options.export:
        album = ExportLibrary(su.expand_home_folder(options.export))
        export_iphoto(album, data, options.exclude, options)
    if options.picasaweb:
        try:
            import phoshare.picasaweb as picasaweb
            albums = picasaweb.PicasaAlbums(options.picasaweb, google_password)
            export_iphoto(albums, data, options.exclude, options)
        except ImportError:
            su.perr('Sorry, this version of Phoshare does not support uploading to PicasaWeb.')
Exemple #14
0
def update_iptcdata(filepath, new_caption, new_keywords, new_datetime,
                    new_rating, new_gps, new_rectangles, new_persons):
    """Updates the caption and keywords of an image file."""
    # Some cameras write into ImageDescription, so we wipe it out to not cause
    # conflicts with Caption-Abstract. We also wipe out the XMP Subject and
    # Description tags (we use Keywords and Caption-Abstract).
    command = [
        EXIFTOOL, '-F', '-m', '-P', '-ImageDescription=', '-Subject=',
        '-Description='
    ]
    tmp = None
    if not new_caption is None:
        if not new_caption:
            command.append('-Caption-Abstract=')
        else:
            tmpfd, tmp = tempfile.mkstemp(dir="/var/tmp")
            os.close(tmpfd)
            file1 = open(tmp, "w")
            print >> file1, new_caption.encode("utf-8")
            file1.close()
            command.append('-Caption-Abstract<=%s' % (tmp))

    if new_datetime:
        command.append('-DateTimeOriginal="%s"' %
                       (new_datetime.strftime("%Y:%m:%d %H:%M:%S")))
    if new_keywords:
        for keyword in new_keywords:
            command.append(u'-keywords=%s' % (keyword))
    elif new_keywords != None:
        command.append('-keywords=')
    if new_rating >= 0:
        command.append('-Rating=%d' % (new_rating))
    if new_gps:
        command.append('-c')
        command.append('%.6f')
        command.append('-GPSLatitude="%f"' % (abs(new_gps.latitude)))
        command.append('-GPSLatitudeRef=' + new_gps.latitude_ref())
        command.append('-GPSLongitude="%f"' % (abs(new_gps.longitude)))
        command.append('-GPSLongitudeRef=' + new_gps.longitude_ref())
    if new_persons:
        for person in new_persons:
            command.append(u'-RegionPersonDisplayName=%s' % (person))
    elif new_persons != None:
        command.append('-RegionPersonDisplayName=')
    if new_rectangles:
        for rectangle in new_rectangles:
            command.append('-RegionRectangle=%s' %
                           (','.join(str(c) for c in rectangle)))
    elif new_rectangles != None:
        command.append('-RegionRectangle=')
    command.append("-iptc:CodedCharacterSet=ESC % G")
    command.append(filepath)
    result = su.fsdec(su.execandcombine(command))
    if tmp:
        os.remove(tmp)
    if result.find("1 image files updated") != -1:
        if result != "1 image files updated":
            su.pout(result)

        # wipe out the back file created by exiftool
        backup_file = filepath + "_original"
        if os.path.exists(backup_file):
            os.remove(backup_file)
        return True
    else:
        su.perr("Failed to update IPTC data in image %s: %s" %
                (filepath, result))
        return False
Exemple #15
0
    command.append("-iptc:CodedCharacterSet=ESC % G")
    command.append(filepath)
    result = su.fsdec(su.execandcombine(command))
    if tmp:
        os.remove(tmp)
    if result.find("1 image files updated") != -1:
        if result != "1 image files updated":
            su.pout(result)

        # wipe out the back file created by exiftool
        backup_file = filepath + "_original"
        if os.path.exists(backup_file):
            os.remove(backup_file)
        return True
    else:
        su.perr("Failed to update IPTC data in image %s: %s" %
                (filepath, result))
        return False


def _write_caption_file(new_caption, command):
    """If new_caption is set, write it into a tempory file, add a parameter to
       command, and return the file handle."""
    if new_caption is None:
        return None
    if not new_caption:
        command.append('-Caption-Abstract=')
        command.append('-ImageDescription=')
        return None
    tmpfd, tmp = tempfile.mkstemp(dir="/var/tmp")
    os.close(tmpfd)
    file1 = open(tmp, "w")
    command.append("-iptc:CodedCharacterSet=ESC % G")
    command.append(filepath)
    result = su.fsdec(su.execandcombine(command))
    if tmp:
        os.remove(tmp)
    if result.find("1 image files updated") != -1:
        if result != "1 image files updated":
            su.pout(result)
           
        # wipe out the back file created by exiftool
        backup_file = filepath + "_original"
        if os.path.exists(backup_file):
            os.remove(backup_file)
        return True
    else:
        su.perr("Failed to update IPTC data in image %s: %s" % (
            filepath, result))
        return False

def _write_caption_file(new_caption, command):
    """If new_caption is set, write it into a tempory file, add a parameter to
       command, and return the file handle."""
    if new_caption is None:
        return None
    if not new_caption:
        command.append('-Caption-Abstract=')
        command.append('-ImageDescription=')
        return None
    tmpfd, tmp = tempfile.mkstemp(dir="/var/tmp")
    os.close(tmpfd)
    file1 = open(tmp, "w")
    file1.write(new_caption.encode("utf-8"))
Exemple #17
0
def update_iptcdata(filepath, new_caption, new_keywords, new_datetime,
                    new_rating, new_gps, new_rectangles, new_persons):
    """Updates the caption and keywords of an image file."""
    # Some cameras write into ImageDescription, so we wipe it out to not cause
    # conflicts with Caption-Abstract. We also wipe out the XMP Subject and
    # Description tags (we use Keywords and Caption-Abstract).
    command = [EXIFTOOL, '-F', '-m', '-P', '-ImageDescription=', '-Subject=',
               '-Description=']
    tmp = None
    if not new_caption is None:
        if not new_caption:
            command.append('-Caption-Abstract=')
        else:
            tmpfd, tmp = tempfile.mkstemp(dir="/var/tmp")
            os.close(tmpfd)
            file1 = open(tmp, "w")
            print >> file1, new_caption.encode("utf-8")
            file1.close()
            command.append('-Caption-Abstract<=%s' % (tmp))
    
    if new_datetime:
        command.append('-DateTimeOriginal="%s"' % (
            new_datetime.strftime("%Y:%m:%d %H:%M:%S")))
    if new_keywords:
        for keyword in new_keywords:
            command.append(u'-keywords=%s' % (keyword))
    elif new_keywords != None:
        command.append('-keywords=')
    if new_rating >= 0:
        command.append('-Rating=%d' % (new_rating))
    if new_gps:
        command.append('-c')
        command.append('%.6f')
        command.append('-GPSLatitude="%f"' % (abs(new_gps.latitude)))
        command.append('-GPSLatitudeRef=' + new_gps.latitude_ref())
        command.append('-GPSLongitude="%f"' % (abs(new_gps.longitude)))
        command.append('-GPSLongitudeRef=' + new_gps.longitude_ref())
    if new_persons:
        for person in new_persons:
            command.append(u'-RegionPersonDisplayName=%s' % (person))
    elif new_persons != None:
        command.append('-RegionPersonDisplayName=')
    if new_rectangles:
        for rectangle in new_rectangles:
            command.append('-RegionRectangle=%s' % (
                ','.join(str(c) for c in rectangle)))
    elif new_rectangles != None:
        command.append('-RegionRectangle=')
    command.append("-iptc:CodedCharacterSet=ESC % G")
    command.append(filepath)
    result = su.fsdec(su.execandcombine(command))
    if tmp:
        os.remove(tmp)
    if result.find("1 image files updated") != -1:
        if result != "1 image files updated":
            su.pout(result)
            
        # wipe out the back file created by exiftool
        backup_file = filepath + "_original"
        if os.path.exists(backup_file):
            os.remove(backup_file)
        return True
    else:
        su.perr("Failed to update IPTC data in image %s: %s" % (
            filepath, result))
        return False
Exemple #18
0
def get_iptc_data(image_file):
    """get caption, keywords, datetime, rating, and GPS info all in one 
       operation."""
    output = su.execandcombine(
        (EXIFTOOL, "-X", "-m", "-q", "-q", '-c', '%.6f', "-Keywords",
         "-Caption-Abstract", "-DateTimeOriginal", "-Rating", "-GPSLatitude",
         "-Subject", "-GPSLongitude", "-RegionRectangle",
         "-RegionPersonDisplayName", image_file))

    keywords = []
    caption = None
    date_time_original = None
    rating = 0
    gps = None
    region_names = []
    region_rectangles = []
    if output:
        try:
            gps_latitude = None
            gps_longitude = None

            xml_data = minidom.parseString(output)

            for xml_desc in xml_data.getElementsByTagName('rdf:Description'):
                _get_xml_nodevalues(xml_desc, 'IPTC:Keywords', keywords)
                # Keywords can also be stored as Subject in the XMP directory
                _get_xml_nodevalues(xml_desc, 'XMP:Subject', keywords)
                for xml_caption in xml_data.getElementsByTagName(
                        'IPTC:Caption-Abstract'):
                    caption = xml_caption.firstChild.nodeValue
                for xml_element in xml_data.getElementsByTagName(
                        'ExifIFD:DateTimeOriginal'):
                    if not xml_element.firstChild:
                        continue
                    try:
                        date_time_original = time.strptime(
                            xml_element.firstChild.nodeValue,
                            '%Y:%m:%d %H:%M:%S')
                        date_time_original = datetime.datetime(
                            date_time_original.tm_year,
                            date_time_original.tm_mon,
                            date_time_original.tm_mday,
                            date_time_original.tm_hour,
                            date_time_original.tm_min,
                            date_time_original.tm_sec)
                    except ValueError, _ve:
                        su.perr('Exiftool returned an invalid '
                                'date %s for %s - ignoring.' %
                                (xml_element.firstChild.nodeValue, image_file))
                for xml_element in xml_data.getElementsByTagName(
                        'XMP-xmp:Rating'):
                    rating = int(xml_element.firstChild.nodeValue)
                for xml_element in xml_data.getElementsByTagName(
                        'Composite:GPSLatitude'):
                    gps_latitude = xml_element.firstChild.nodeValue
                for xml_element in xml_data.getElementsByTagName(
                        'Composite:GPSLongitude'):
                    gps_longitude = xml_element.firstChild.nodeValue
                string_rectangles = []
                _get_xml_nodevalues(xml_desc, 'XMP-MP:RegionRectangle',
                                    string_rectangles)
                for string_rectangle in string_rectangles:
                    rectangle = []
                    for c in string_rectangle.split(','):
                        rectangle.append(float(c))
                    region_rectangles.append(rectangle)
                _get_xml_nodevalues(xml_desc, 'XMP-MP:RegionPersonDisplayName',
                                    region_names)

            xml_data.unlink()
            if gps_latitude and gps_longitude:
                gps = imageutils.GpsLocation().from_composite(
                    gps_latitude, gps_longitude)
        except parsers.expat.ExpatError, ex:
            su.perr('Could not parse exiftool output %s: %s' % (output, ex))