--- exif.module 2007-05-29 00:21:06.000000000 +1000 +++ exif.module.new 2008-10-21 13:47:40.000000000 +1100 @@ -1,5 +1,6 @@ images['_original']); + if (!file_exists($file)) { + watchdog('exif', t('Image %file not found.', array('%file' => $file)), WATCHDOG_WARNING); + } + if (!is_file($file)) return FALSE; + if (exif_imagetype($file) != IMAGETYPE_JPEG) return FALSE; + + return $file; +} + +function _exif_DMS2D($entry) { + $value = $entry->getValue(); + $degrees = $value[0][0] / $value[0][1]; + $minutes = $value[1][0] / $value[1][1]; + $seconds = $value[2][0] / $value[2][1]; + + return $degrees + $minutes/60 + $seconds/3600; +} + +function _exif_fill_node_fields(&$node) { +/* +** As we only care about location right now, don't bother if +** that module is installed or the location seems filled. +*/ + + // Don't bother is location is not installed. + if (!module_exists('location')) return FALSE; + // Don't bother if we don't save locations for image + if (variable_get('location_maxnum_image', 0) < 1) return FALSE; + // Don't bother is this node's location is already set. + if (is_array($node->locations) && + $node->locations[0]['latitude'] != 0) return FALSE; + + $file = _exif_node_file($node); + if (!$file) return; + + $jpeg = new PelJpeg($file); + $exif = $jpeg->getExif(); + if (!$exif) return FALSE; + $tiff = $exif->getTiff(); + if (!$tiff) return FALSE; + $ifd0 = $tiff->getIfd(); + if (!$ifd0) return FALSE; + $gps = $ifd0->getSubIfd(PelIfd::GPS); + if (!$gps) return FALSE; + + $entry = $gps->getEntry(PelTag::GPS_LATITUDE); + if (!$entry) return FALSE; + $GPS_LATITUDE = _exif_DMS2D($entry); + $entry = $gps->getEntry(PelTag::GPS_LATITUDE_REF); + if (!$entry) return FALSE; + if ($entry->getValue() == 'S') $GPS_LATITUDE = -$GPS_LATITUDE; + + $entry = $gps->getEntry(PelTag::GPS_LONGITUDE); + if (!$entry) return FALSE; + $GPS_LONGITUDE = _exif_DMS2D($entry); + $entry = $gps->getEntry(PelTag::GPS_LONGITUDE_REF); + if (!$entry) return FALSE; + if ($entry->getValue() == 'W') $GPS_LONGITUDE = -$GPS_LONGITUDE; + + $node->locations[0]['latitude'] = $GPS_LATITUDE; + $node->locations[0]['longitude'] = $GPS_LONGITUDE; + $node->locations['lat'] = $GPS_LATITUDE; + $node->locations['lon'] = $GPS_LONGITUDE; + + return TRUE; +} + /** * Implementation of hook_nodeapi(). */ @@ -36,11 +107,9 @@ function exif_nodeapi(&$node, $op, $teas switch ($op) { case 'view': // Access the file's EXIF data. Simply break when no EXIF data is found. - $file = file_create_path($node->images['_original']); - if (!file_exists($file)) { - watchdog('exif', t('Image %file not found.', array('%file' => $file)), WATCHDOG_WARNING); - } - if (!is_file($file)) break; + $file = _exif_node_file($node); + if (!$file) break; + $jpeg = new PelJpeg($file); $exif = $jpeg->getExif(); if (!$exif) break; @@ -57,7 +126,7 @@ function exif_nodeapi(&$node, $op, $teas } // Retrieve the desired tag values from the file - $tags = exif_get_enabled_tags(); + $tags = _exif_get_enabled_tags(); $rows = array(); foreach ($tags as $tag) { if (isset($ifds[$tag->ifd])) { @@ -88,14 +157,21 @@ function exif_nodeapi(&$node, $op, $teas ); } break; - } + +/* For updates, extract any info from EXIF and save if none already given */ + + case 'submit': + case 'update': + _exif_fill_node_fields($node); + break; + } } /** * Administration page callback. */ function exif_admin_settings() { - $tags = exif_load_settings(); + $tags = _exif_load_settings(); return drupal_get_form('exif_admin_settings_form', $tags); } @@ -126,6 +202,53 @@ function exif_admin_settings_form($tags) $form['tags']["{$tag->ifd}_{$tag->tag}"]['#tree'] = TRUE; $form['tags']["{$tag->ifd}_{$tag->tag}"]['#weight'] = $tag->weight; } + + $form['harvest'] = array( + '#type' => 'fieldset', + '#title' => t('Node Completion'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#description' => t('When creating image nodes, what fields can be automatically harvested from the EXIF tags if not entered by the user.'), + ); + + /* The idea is that more than just location can be used in the future */ + $form['harvest']['location'] = array( + '#type' => 'checkbox', + '#title' => t('Location'), + '#default_value' => variable_get('exif_harvest_location', 0), + '#description' => sprintf(t('From GPS co-ordinates. Requires Location module (%s).'), + module_exists('location') ? t('enabled') + : t('disabled/missing')), + '#disabled' => !module_exists('location') + ); + + + /* Figure out the scan status */ + $scan_to = variable_get('exif_scan_to', 0); + $scan_last = variable_get('exif_scan_last', 0); + + if ($scan_last == $scan_to) { + $scan_status = sprintf(t("Finished (scanned to %d)"), $scan_to); + } else if ($scan_last < 1 && $scan_to > 0) { + $scan_status = sprintf(t("Not started (%d of %d)"), $scan_last, $scan_to); + } else if ($scan_last < $scan_to) { + $scan_status = sprintf(t("In progress (%d of %d)"), $scan_last, $scan_to); + } else { + $scan_status = t('Not started'); + } + + $form['harvest']['rescan'] = array( + '#type' => 'markup', + '#value' => t('
The Exif module can re-scan images (done as part of cron processing) and perform node completion functions based on the above rules. This is useful when the module is first installed.
') + ); + + $form['harvest']['start'] = array( + '#type' => 'checkbox', + '#title' => t('Start Scan'), + '#default_value' => FALSE, + '#description' => t('Check this box and submit to start scan. Status is currently: ') . $scan_status + ); + $form['buttons']['submit'] = array( '#type' => 'submit', '#value' => t('Save configuration') @@ -142,11 +265,11 @@ function exif_admin_settings_form_submit $op = isset($_POST['op']) ? $_POST['op'] : ''; if ($op == t('Reset to defaults')) { - exif_reset_settings(); + _exif_reset_settings(); drupal_set_message(t('The configuration options have been reset to their default values.')); } elseif ($op == t('Save configuration')) { - exif_save_settings($values); + _exif_save_settings($values); drupal_set_message(t('The configuration options have been saved.')); } } @@ -154,7 +277,7 @@ function exif_admin_settings_form_submit /** * Return an array containing only the tags that were enabled. */ -function exif_get_enabled_tags() { +function _exif_get_enabled_tags() { static $tags = array(); if (!count($tags)) { @@ -164,7 +287,7 @@ function exif_get_enabled_tags() { } if (!count($tags)) { // Table is empty, get some defaults - $tags = exif_get_default_settings(); + $tags = _exif_get_default_settings(); foreach ($tags as $key => $tag) { if (!$tag->status) { unset($tags[$key]); @@ -180,8 +303,8 @@ function exif_get_enabled_tags() { /** * Return an array with all the valid tags and their settings. */ -function exif_load_settings() { - $tags = exif_get_default_settings(); +function _exif_load_settings() { + $tags = _exif_get_default_settings(); $result = db_query('SELECT * FROM {exif}'); while ($tag = db_fetch_object($result)) { @@ -192,10 +315,19 @@ function exif_load_settings() { return $tags; } -function exif_save_settings($values) { +function _exif_save_settings($values) { db_lock_table('exif'); - foreach ($values as $tag) { + foreach ($values as $name => $tag) { if (!is_array($tag) || !isset($tag['ifd'])) { + /* Handle 'standard' options */ + if ($name == 'location') { + variable_set('exif_harvest_location', $tag); + } else if ($name = 'start' && $tag) { + /* Reset vars to restart scan */ + variable_set('exif_scan_last', 0); + variable_set('exif_scan_from', 1); + variable_set('exif_scan_to', 3216); + } continue; // Save only appropriate form values } db_query('DELETE FROM {exif} WHERE ifd = %d AND tag = %d', $tag['ifd'], $tag['tag']); @@ -204,15 +336,15 @@ function exif_save_settings($values) { db_unlock_tables(); } -function exif_reset_settings() { +function _exif_reset_settings() { db_query('DELETE FROM {exif}'); } /** * Return an array with all the valid tags, with some useful default settings. */ -function exif_get_default_settings() { - $tags = exif_get_valid_tags(); +function _exif_get_default_settings() { + $tags = _exif_get_valid_tags(); $tags[PelIfd::EXIF .'_'. PelTag::DATE_TIME_ORIGINAL]->status = 1; $tags[PelIfd::EXIF .'_'. PelTag::DATE_TIME_ORIGINAL]->weight = -10; $tags[PelIfd::IFD0 .'_'. PelTag::MODEL]->status = 1; @@ -234,7 +366,7 @@ function exif_get_default_settings() { * For convenience, each tag has a key in the form: "ifd_tag", where ifd and tag * are standard Exif ids. Those ids can be found in PelIfd.php and PelTag.php. */ -function exif_get_valid_tags() { +function _exif_get_valid_tags() { $valid_tags = array(); $valid_tags = array_merge($valid_tags, _exif_get_valid_ifd_tags(PelIfd::IFD0, new PelIfd(PelIfd::IFD0))); $valid_tags = array_merge($valid_tags, _exif_get_valid_ifd_tags(PelIfd::EXIF, new PelIfd(PelIfd::EXIF))); @@ -293,3 +425,32 @@ function theme_exif_admin_settings_form( return $output; } +/** + * Cron - rescan images for location info if we've been directed to. + */ + +function exif_cron() { + /* Don't bother if we're done or not started */ + $scan_to = variable_get('exif_scan_to', 0); + $scan_last = variable_get('exif_scan_last', 0); + + if ($scan_to == 0 or $scan_last >= $scan_to) return; + + /* Continue where we left off - or start 'from' if not done before */ + if ($scan_last == 0) $scan_last = variable_get('exif_scan_from', 0); + + /* Don't be too greedy with the execution time - take one quarter */ + $time_limit = time() + ini_get("max_execution_time") / 4; + + while ($scan_last++ < $scan_to && time() < $time_limit) { + $node = node_load($scan_last); + /* If no node loaded, or not an image we don't care */ + if ($node->nid && $node->type == 'image') { + /* Save if we filled anything */ + if (_exif_fill_node_fields($node)) { + location_nodeapi($node, "update"); + } + } + variable_set('exif_scan_last', $scan_last); + } +}