? .audio.module.swp
? audio_image.patch
? db_audio.sql
Index: audio.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/audio/audio.install,v
retrieving revision 1.22
diff -u -p -r1.22 audio.install
--- audio.install	14 Aug 2008 20:42:04 -0000	1.22
+++ audio.install	1 Oct 2008 23:46:40 -0000
@@ -41,6 +41,12 @@ function audio_schema() {
         'not null' => TRUE,
         'default' => 0,
       ),
+      'fid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => t('Primary Key: The {files}.fid.'),
+      ),
       'title_format' => array(
         'type' => 'varchar',
         'length' => 128,
@@ -64,36 +70,12 @@ function audio_schema() {
         'not null' => TRUE,
         'default' => 1,
       ),
-      'file_format' => array(
+      'format' => array(
         'type' => 'varchar',
         'length' => 10,
         'not null' => TRUE,
         'default' => '',
       ),
-      'file_mime' => array(
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'file_name' => array(
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'file_path' => array(
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'file_size' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ),
       'sample_rate' => array(
         'type' => 'int',
         'size' => 'medium',
@@ -126,6 +108,9 @@ function audio_schema() {
       ),
     ),
     'primary key' => array('vid'),
+    'indexes' => array(
+      'audio_fid' => array('fid'),
+    ),
   );
   $schema['audio_metadata'] = array(
     'description' => t('Extended data about audio files.'),
@@ -533,3 +518,50 @@ function audio_update_6001() {
   db_add_primary_key($ret, 'audio_metadata', array('vid', 'tag', 'value'));
   return $ret;
 }
+
+/**
+ * Move the audio files from the {audio} table to the {files} table and then
+ * remove the columns.
+ *
+ * @return unknown
+ */
+function audio_update_6002() {
+  $ret = array();
+
+  // Create the fid field so we've got a place to store the file id's as we
+  // migrate them.
+  $fid_field = array(
+    'type' => 'int',
+    'unsigned' => TRUE,
+    'not null' => TRUE,
+    'description' => t('Primary Key: The {files}.fid.'),
+  );
+  db_add_field($ret, 'audio', 'fid', $fid_field);
+  db_add_index($ret, 'audio', 'audio_fid', array('fid'));
+
+  // Load all the distinct filepaths, note that this may update multiple audio
+  // rows at once.
+  $result = db_query("SELECT DISTINCT file_path FROM {audio} WHERE fid IS NULL OR fid = 0");
+  while ($file = db_fetch_object($result)) {
+    // Then move the data into the files table.
+    db_query("INSERT INTO {files} (uid, filename, filepath, filemime, filesize, status, timestamp) SELECT n.uid, '%s', ai.file_path, ai.file_mime, ai.file_size, 1, n.created AS timestamp FROM {node} n INNER JOIN {audio} ai ON n.vid = ai.vid WHERE ai.file_path = '%s' LIMIT 1", array(basename($file->file_path), $file->file_path));
+    db_query("UPDATE {audio} SET fid = %d WHERE file_path = '%s'", db_last_insert_id('files', 'fid'), $file->file_path);
+  }
+
+  // Drop the old fields.
+  db_drop_field($ret, 'audio', 'file_mime');
+  db_drop_field($ret, 'audio', 'file_name');
+  db_drop_field($ret, 'audio', 'file_path');
+  db_drop_field($ret, 'audio', 'file_size');
+
+  // Rename the file_format field to format.
+  $format_field = array(
+    'type' => 'varchar',
+    'length' => 10,
+    'not null' => TRUE,
+    'default' => '',
+  );
+  db_change_field($ret, 'audio', 'file_format', 'format', $format_field);
+
+  return $ret;
+}
\ No newline at end of file
Index: audio.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/audio/audio.module,v
retrieving revision 1.144
diff -u -p -r1.144 audio.module
--- audio.module	30 Sep 2008 05:00:52 -0000	1.144
+++ audio.module	1 Oct 2008 23:46:40 -0000
@@ -276,7 +276,7 @@ function audio_access($op, $node = NULL)
  *   boolean indicating if it's allowed.
  */
 function _audio_allow_download($node) {
-  if ($node->type == 'audio' && isset($node->url_download) && $node->audio_file['downloadable']) {
+  if ($node->type == 'audio' && isset($node->url_download) && $node->audio['downloadable']) {
     $result = audio_invoke_audioapi('access', $node, 'download');
 
     if (in_array(TRUE, $result)) {
@@ -341,13 +341,13 @@ function audio_link($type, $node, $main 
       );
       if ($link_access) {
         $links['audio_download_count'] = array(
-          'title' => t('@download_count downloads', array('@download_count' => $node->audio_file['download_count'])),
+          'title' => t('@download_count downloads', array('@download_count' => $node->audio['download_count'])),
         );
       }
     }
     if (_audio_allow_play($node) && $link_access) {
       $links['audio_play_count'] = array(
-        'title' => t('@play_count plays', array('@play_count' => $node->audio_file['play_count'])),
+        'title' => t('@play_count plays', array('@play_count' => $node->audio['play_count'])),
       );
     }
   }
@@ -377,8 +377,8 @@ function audio_nodeapi(&$node, $op, $arg
             'key' => 'enclosure',
             'attributes' => array(
               'url' => $node->url_download,
-              'length' => $node->audio_file['file_size'],
-              'type' => $node->audio_file['file_mime']
+              'length' => $node->audio['file']->filesize,
+              'type' => $node->audio['file']->filemime
             ));
           // Provide very basic iTunes support.
           $ret[] = array(
@@ -386,7 +386,7 @@ function audio_nodeapi(&$node, $op, $arg
           );
           $ret[] = array(
             'key' => 'itunes:duration',
-            'value' => $node->audio_file['playtime'],
+            'value' => $node->audio['playtime'],
           );
           $ret[] = array(
             'key' => 'itunes:author',
@@ -433,8 +433,7 @@ function audio_view(&$node, $teaser = FA
  * Implementation of hook_validate().
  */
 function audio_validate(&$node, &$form) {
-  $nid = ($node->nid) ? $node->nid : 'new_node';
-  if (!isset($node->audio_file['file_path'])) {
+  if (!isset($node->audio['file']->filepath)) {
     form_set_error('audio_upload', t("A file must be provided. If you tried uploading a file, make sure it's less than the upload size limit."));
   }
 
@@ -450,35 +449,34 @@ function audio_load($node) {
   if ($node->vid) {
     // This is a wonky way to load the fields but its handy right now while
     // I'm renaming fields in the databases.
-    $fields = db_fetch_array(db_query("SELECT * FROM {audio} WHERE vid=%d", $node->vid));
+    $fields = db_fetch_array(db_query("SELECT a.* FROM {audio} a WHERE vid=%d", $node->vid));
+    $file = db_fetch_object(db_query("SELECT * FROM {files} WHERE fid=%d", $fields['fid']));
+
     $ret = array(
       'title_format' => $fields['title_format'],
-      'audio_file' => array(
+      'audio' => array(
         'play_count'     => $fields['play_count'],
         'download_count' => $fields['download_count'],
         'downloadable'   => $fields['downloadable'],
-        'file_format'    => $fields['file_format'],
-        'file_mime'      => $fields['file_mime'],
-        'file_name'      => $fields['file_name'],
-        'file_path'      => $fields['file_path'],
-        'file_size'      => $fields['file_size'],
+        'format'         => $fields['format'],
         'sample_rate'    => $fields['sample_rate'],
         'channel_mode'   => $fields['channel_mode'],
         'bitrate'        => $fields['bitrate'],
         'bitrate_mode'   => $fields['bitrate_mode'],
         'playtime'       => $fields['playtime'],
         'bitrate'        => $fields['bitrate'],
+        'file'           => $file,
       ),
     );
 
-    if (file_exists($fields['file_path'])) {
+    if (isset($file->filepath) && file_exists($file->filepath)) {
       // TODO: should these links be by vid?
       $ret['url_play'] = url('audio/play/'. $node->nid, array('absolute' => TRUE));
-      if ($ret['audio_file']['downloadable']) {
+      if ($ret['audio']['downloadable']) {
         // iTunes and other podcasting programs check the url to determine the
         // file type. we'll add the original file name on to the end. see issues
         // #35398 and #68716 for more info.
-        $url = 'audio/download/'. $node->nid .'/'. $fields['file_name'];
+        $url = 'audio/download/'. $node->nid .'/'. $file->filename;
         $ret['url_download'] = url($url, array('absolute' => TRUE));
       }
     }
@@ -499,83 +497,75 @@ function audio_load($node) {
  * Implementation of hook_insert().
  */
 function audio_insert(&$node) {
-  file_move($node->audio_file['file_path'], audio_get_directory(), FILE_EXISTS_RENAME);
-
-  $f = $node->audio_file;
-  db_query("INSERT INTO {audio} (vid, nid, title_format, downloadable, file_format, file_name, file_path, file_mime, file_size, bitrate, bitrate_mode, sample_rate, channel_mode, playtime)
-    VALUES (%d, %d, '%s', %d, '%s', '%s', '%s', '%s', %d, %f, '%s', %d, '%s', '%s')",
-    $node->vid, $node->nid, $node->title_format, $f['downloadable'], $f['file_format'], $f['file_name'], $f['file_path'], $f['file_mime'], filesize($f['file_path']), $f['bitrate'], $f['bitrate_mode'], $f['sample_rate'], $f['channel_mode'], $f['playtime']);
-
-  _audio_save_tags_to_db($node);
-
-  // Unset the new file flag incase this node is re-saved.
-  unset($node->audio_file['newfile']);
+  file_move($node->audio['file']->filepath, audio_get_directory(), FILE_EXISTS_RENAME);
+  _audio_save($node);
 }
 
 /**
- * Insert a new node revision.
- *
- * If a new file wasn't uploaded, make a copy of the existing file.
+ * Implementation of hook_update().
  */
-function audio_insert_revision($node) {
-  if (isset($node->audio_file['newfile'])) {
-    file_move($node->audio_file['file_path'], audio_get_directory(), FILE_EXISTS_RENAME);
+function audio_update(&$node) {
+  // If there's a new file we need to move it to the correct location.
+  if (isset($node->audio['newfile'])) {
+    if (!$node->revision) {
+      // If it's not a revision the existing file needs to be removed.
+      $oldfile = db_fetch_object(db_query('SELECT f.* FROM {files} f INNER JOIN {audio} a ON f.fid = a.fid WHERE a.vid = %d', $node->vid));
+      _audio_file_delete($oldfile);
+    }
+    file_move($node->audio['file']->filepath, audio_get_directory(), FILE_EXISTS_RENAME);
   }
-  else {
-    $newname = file_create_filename($node->audio_file['file_name'], audio_get_directory());
-    file_copy($node->audio_file['file_path'], $newname);
+  else if ($node->revision) {
+    // New revision using existing file so we need to copy it.
+    file_copy($node->audio['file']->filepath, file_create_filename($node->audio['file']->filename, audio_get_directory()));
+    // Unset the fid so a new record is created.
+    $node->audio['file']->fid = NULL;
   }
 
-  $f = $node->audio_file;
-  db_query("INSERT INTO {audio} (vid, nid, title_format, downloadable, file_format, file_name, file_path, file_mime, file_size, bitrate, bitrate_mode, sample_rate, channel_mode, playtime)
-    VALUES (%d, %d, '%s', %d, '%s', '%s', '%s', '%s', %d, %f, '%s', %d, '%s', '%s')",
-    $node->vid, $node->nid, $node->title_format, $f['downloadable'], $f['file_format'], $f['file_name'], $f['file_path'], $f['file_mime'], filesize($f['file_path']), $f['bitrate'], $f['bitrate_mode'], $f['sample_rate'], $f['channel_mode'], $f['playtime']);
-
-  _audio_save_tags_to_db($node);
+  _audio_save($node, array('vid'));
 }
 
 /**
- * Implementation of hook_update().
+ * Handle the busy work of saving the node's audio and file records.
+ *
+ * @param $node Node opject.
+ * @param $update Array of fields to match for an update, empty array inserts
+ *   a new record.
  */
-function audio_update(&$node) {
-  // Check for new revsion.
-  if ($node->revision) {
-    return audio_insert_revision($node);
-  }
+function _audio_save(&$node, $update = array()) {
+  // Save the file
+  $node->audio['file']->timestamp = time();
+  $node->audio['file']->filesize = filesize($node->audio['file']->filepath);
+  $node->audio['file']->status |= FILE_STATUS_PERMANENT;
+  // If there's an fid update, otherwise insert.
+  drupal_write_record('files', $node->audio['file'], empty($node->audio['file']->fid) ? array() : array('fid'));
 
-  if (isset($node->audio_file['newfile'])) {
-    unset($node->audio_file['newfile']);
+  // Save the audio row.
+  $audio = array_merge($node->audio, array('nid' => $node->nid, 'vid' => $node->vid, 'title_format' => $node->title_format, 'fid' => $node->audio['file']->fid));
+  drupal_write_record('audio', $audio, $update);
 
-    // Remove the old file.
-    $oldfile = db_fetch_object(db_query('SELECT file_path FROM {audio} f WHERE f.vid = %d', $node->vid));
-    file_delete($oldfile->file_path);
+  // Remove any existing metadata.
+  db_query("DELETE FROM {audio_metadata} WHERE vid=%d", $node->vid);
 
-    // Save the new one.
-    file_move($node->audio_file['file_path'], audio_get_directory(), FILE_EXISTS_RENAME);
+  // Save the new tags.
+  $allowed_tags = audio_get_tags_allowed();
+  foreach ($node->audio_tags as $tag => $value) {
+    if (in_array($tag, $allowed_tags) && $value) {
+      $metadata = array('vid' => $node->vid, 'tag' => $tag, 'value' => $value, 'clean' => audio_clean_tag($value));
+      drupal_write_record('audio_metadata', $metadata);
+    }
   }
 
-  // Do a delete and insert rather than an update to ensure that we've got db
-  // records. it takes a bit longer but it allows us to carry on if one of the
-  // records wasn't created by audio_insert().
-  db_query("DELETE FROM {audio} WHERE vid=%d", $node->vid);
-
-  $f = $node->audio_file;
-  db_query("INSERT INTO {audio} (vid, nid, title_format, play_count, download_count, downloadable, file_format, file_name, file_path, file_mime, file_size, bitrate, bitrate_mode, sample_rate, channel_mode, playtime)
-    VALUES (%d, %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s', %d, %f, '%s', %d, '%s', '%s')",
-    $node->vid, $node->nid, $node->title_format, $f['play_count'], $f['download_count'], $f['downloadable'], $f['file_format'], $f['file_name'], $f['file_path'], $f['file_mime'], filesize($f['file_path']), $f['bitrate'], $f['bitrate_mode'], $f['sample_rate'], $f['channel_mode'], $f['playtime']);
-
-  _audio_save_tags_to_db($node);
+  // Unset the new file flag incase this node is re-saved.
+  unset($node->audio['newfile']);
 }
 
 /**
  * Implementation of hook_delete().
  */
 function audio_delete($node) {
-  $result = db_query('SELECT vid, file_path FROM {audio} WHERE nid = %d', $node->nid);
+  $result = db_query('SELECT a.vid, f.* FROM {audio} a LEFT JOIN {files} f ON a.fid = f.fid WHERE nid = %d', $node->nid);
   while ($o = db_fetch_object($result)) {
-    if (isset($o->file_path)) {
-      file_delete($o->file_path);
-    }
+    _audio_file_delete($o);
     db_query('DELETE FROM {audio_metadata} WHERE vid = %d', $o->vid);
   }
   db_query('DELETE FROM {audio} WHERE nid = %d', $node->nid);
@@ -585,15 +575,28 @@ function audio_delete($node) {
  * Delete a single revision.
  */
 function audio_delete_revision($node) {
-  if ($filepath = db_result(db_query('SELECT file_path FROM {audio} WHERE vid = %d', $node->vid))) {
-    file_delete($filepath);
-  }
+  $file = db_result(db_query('SELECT a.vid, f.* FROM {audio} a LEFT JOIN {files} f ON a.fid = f.fid WHERE vid = %d', $node->vid));
+  _audio_file_delete($file);
 
   db_query('DELETE FROM {audio_metadata} WHERE vid = %d', $node->vid);
   db_query('DELETE FROM {audio} WHERE vid = %d', $node->vid);
 }
 
 /**
+ * Handle the busy work of deleting the file record and the file.
+ *
+ * @param unknown_type $file
+ */
+function _audio_file_delete($file) {
+  if (!empty($file->filepath)) {
+    file_delete($file->filepath);
+  }
+  if (!empty($file->fid)) {
+    db_query('DELETE FROM {files} WHERE fid = %d', array($file->fid));
+  }
+}
+
+/**
  * Implementation of hook_form().
  */
 function audio_form(&$node, &$form_state) {
@@ -630,7 +633,7 @@ function audio_form(&$node, &$form_state
   }
 
 
-  $form['audio_file'] = array(
+  $form['audio'] = array(
     '#type' => 'fieldset',
     '#title' => t('Audio File Info'),
     '#collapsible' => TRUE,
@@ -640,112 +643,104 @@ function audio_form(&$node, &$form_state
 
   // Place a visible copy of the file path on the form (after removing the
   // directory info from non-admins).
-  $form['audio_file']['display_file_path'] = array(
+  $form['audio']['display_filepath'] = array(
     '#type' => 'item',
     '#title' => t('Current File'),
-    '#value' => empty($node->audio_file['file_path']) ? t('No file is attached.') : (user_access('administer audio') ? $node->audio_file['file_path'] : basename($node->audio_file['file_path'])),
+    '#value' => empty($node->audio['file']->filepath) ? t('No file is attached.') : (user_access('administer audio') ? $node->audio['file']->filepath : basename($node->audio['file']->filepath)),
   );
 
   // If we've got a file, add the file information fields.
-  if (!empty($node->audio_file['file_name'])) {
-    $form['audio_file']['#theme'] = 'audio_file_form';
+  if (!empty($node->audio['file']->filename)) {
+    $form['audio']['#theme'] = 'audio_file_form';
 
     // Store the non-user editable file information as values.
-    $form['audio_file']['file_path'] = array(
-      '#type' => 'value',
-      '#value' => $node->audio_file['file_path'],
-    );
-    $form['audio_file']['file_name'] = array(
-      '#type' => 'value',
-      '#value' => $node->audio_file['file_name'],
-    );
-    $form['audio_file']['file_mime'] = array(
+    $form['audio']['file'] = array(
       '#type' => 'value',
-      '#value' => $node->audio_file['file_mime'],
+      '#value' => $node->audio['file'],
     );
 
-    $form['audio_file']['file_format'] = array(
+    $form['audio']['format'] = array(
       '#type' => 'select',
       '#title' => t('Format'),
-      '#default_value' => $node->audio_file['file_format'],
+      '#default_value' => $node->audio['format'],
       '#options' => drupal_map_assoc(array('', 'aac', 'ac3', 'au', 'avr', 'flac', 'midi', 'mod', 'mp3', 'mpc', 'ogg', 'voc'), 'drupal_strtoupper'),
     );
-    $form['audio_file']['file_size'] = array(
+    $form['audio']['file_size'] = array(
       '#type' => 'textfield',
       '#title' => t('File Size'),
-      '#default_value' => $node->audio_file['file_size'],
+      '#default_value' => $node->audio['file']->filesize,
     );
-    $form['audio_file']['playtime'] = array(
+    $form['audio']['playtime'] = array(
       '#type' => 'textfield',
       '#title' => t('Length'),
-      '#default_value' => $node->audio_file['playtime'],
+      '#default_value' => $node->audio['playtime'],
       '#description' => t('The format is hours:minutes:seconds.'),
     );
-    $form['audio_file']['sample_rate'] = array(
+    $form['audio']['sample_rate'] = array(
       '#type' => 'select',
       '#title' => t('Sample rate'),
-      '#default_value' => $node->audio_file['sample_rate'],
+      '#default_value' => $node->audio['sample_rate'],
       '#options' => array('' => '', '48000' => '48,000 Hz', '44100' => '44,100 Hz', '32000' => '32,000 Hz', '22050' => '22,050 Hz', '11025' => '11,025 Hz', '8000' => '8,000 Hz'),
     );
-    $form['audio_file']['channel_mode'] = array(
+    $form['audio']['channel_mode'] = array(
       '#type' => 'select',
       '#title' => t('Channel mode'),
-      '#default_value' => $node->audio_file['channel_mode'],
+      '#default_value' => $node->audio['channel_mode'],
       '#options' => array('stereo' => t('Stereo'), 'mono' => t('Mono')),
     );
-    $form['audio_file']['bitrate'] = array(
+    $form['audio']['bitrate'] = array(
       '#type' => 'textfield',
       '#title' => t('Bitrate'),
-      '#default_value' => $node->audio_file['bitrate'],
+      '#default_value' => $node->audio['bitrate'],
     );
-    $form['audio_file']['bitrate_mode'] = array(
+    $form['audio']['bitrate_mode'] = array(
       '#type' => 'select',
       '#title' => t('Bitrate mode'),
-      '#default_value' => $node->audio_file['bitrate_mode'],
+      '#default_value' => $node->audio['bitrate_mode'],
       '#options' => array('' => '', 'cbr' => t('Constant'), 'vbr' => t('Variable')),
     );
 
     // Users shouldn't be able to change the play and download counts so we'll
     // put these for viewing...
-    $form['audio_file']['display_play_count'] = array(
+    $form['audio']['display_play_count'] = array(
       '#type' => 'item',
       '#title' => t('Play count'),
-      '#value' => $node->audio_file['play_count'],
+      '#value' => $node->audio['play_count'],
     );
-    $form['audio_file']['display_download_count'] = array(
+    $form['audio']['display_download_count'] = array(
       '#type' => 'item',
       '#title' => t('Download count'),
-      '#value' => $node->audio_file['download_count'],
+      '#value' => $node->audio['download_count'],
     );
     // ...and these are what we'll save back to the node.
-    $form['audio_file']['play_count'] = array(
+    $form['audio']['play_count'] = array(
       '#type' => 'value',
-      '#value' => $node->audio_file['play_count'],
+      '#value' => $node->audio['play_count'],
     );
-    $form['audio_file']['download_count'] = array(
+    $form['audio']['download_count'] = array(
       '#type' => 'value',
-      '#value' => $node->audio_file['download_count'],
+      '#value' => $node->audio['download_count'],
     );
   }
 
-  $form['audio_file']['audio_upload'] = array(
+  $form['audio']['audio_upload'] = array(
     '#tree' => FALSE,
     '#type' => 'file',
-    '#title' => empty($node->audio_file['file_name']) ? t('Add a new audio file') : t('Replace this with a new file'),
+    '#title' => empty($node->audio['file']->filename) ? t('Add a new audio file') : t('Replace this with a new file'),
     '#description' => t('Click "Browse..." to select an audio file to upload. Only files with the following extensions are allowed: %allowed-extensions.', array('%allowed-extensions' => variable_get('audio_allowed_extensions', 'mp3 wav ogg'))) .'<br />'
       . t('<strong>NOTE:</strong> the current PHP configuration limits uploads to %maxsize.', array('%maxsize' => format_size(file_upload_max_size()))),
   );
-  $form['audio_file']['downloadable'] = array(
+  $form['audio']['downloadable'] = array(
     '#type' => 'checkbox',
     '#title' => t('Allow file downloads.'),
-    '#default_value' => !empty($node->audio_file['downloadable']) ? $node->audio_file['downloadable'] : variable_get('audio_default_downloadable', 1),
+    '#default_value' => !empty($node->audio['downloadable']) ? $node->audio['downloadable'] : variable_get('audio_default_downloadable', 1),
     '#description' => t('If checked, a link will be displayed allowing visitors to download this audio file on to their own computer.') .'<br />'
       . t('<strong>WARNING:</strong> even if you leave this unchecked, clever users will be able to find a way to download the file. This just makes them work a little harder to find the link.'),
   );
 
 
   // If we've got a file, add the fields for editing meta data.
-  if (!empty($node->audio_file['file_path'])) {
+  if (!empty($node->audio['file']->filepath)) {
     $form['audio_tags'] = array(
       '#type' => 'fieldset',
       '#title' => t('Audio Metadata'),
@@ -802,15 +797,12 @@ function audio_node_form_validate($form,
   );
   if ($file = file_save_upload('audio_upload', $validators)) {
     $node = (object) $form_state['values'];
-    $node->audio_file = array(
+    $node->audio = array(
       'newfile' => TRUE,
       'play_count' => 0,
       'download_count' => 0,
-      'downloadable' => (bool) $form_state['values']['audio_file']['downloadable'],
-      'file_name' => $file->filename,
-      'file_path' => $file->filepath,
-      'file_mime' => $file->filemime,
-      'file_size' => $file->filesize,
+      'downloadable' => (bool) $form_state['values']['audio']['downloadable'],
+      'file' => $file,
     );
 
     // Allow other modules to modify the node.
@@ -820,8 +812,9 @@ function audio_node_form_validate($form,
     // form. Note that we do this after calling we called our api hook with
     // the upload operation, it gives the audio_id3 module a chance to read
     // the tags.
-    $form_state['values']['audio_file'] = $node->audio_file;
+    $form_state['values']['audio'] = $node->audio;
     $form_state['values']['audio_tags'] = $node->audio_tags;
+    $form_state['values']['audio_images'] = $node->audio_images;
   }
 }
 
@@ -904,7 +897,7 @@ function audio_user($op, &$edit, &$user,
 function audio_block($op = 'list', $delta = 0, $edit = array()) {
   switch ($op) {
     case 'list':
-      $blocks[2]['info'] = t('audio: Browse by');
+      $blocks[2]['info'] = t('Audio: Browse by');
       return $blocks;
 
     case 'view':
@@ -1089,7 +1082,7 @@ function audio_get_players($op = 'names'
  */
 function audio_get_node_player($node, $playername = NULL) {
   if (_audio_allow_play($node)) {
-    $format = $node->audio_file['file_format'];
+    $format = $node->audio['format'];
     if (!isset($playername)) {
       $playername = variable_get('audio_player_'. $format, '1pixelout');
     }
@@ -1166,7 +1159,16 @@ function audio_api_insert($filepath, $ti
     drupal_access_denied();
   }
 
-  $filepath = realpath($filepath);
+  // Begin building file object.
+  $file = new stdClass();
+  $file->uid = $user->uid;
+  $file->filepath = $filepath;
+  $file->filename = basename($file->filepath);
+  $file->filemime = module_exists('mimedetect') ? mimedetect_mime($file->filepath) : file_get_mimetype($file->filepath);
+  $file->filesize = filesize($file->filepath);
+  $file->timestamp = time();
+  $file->status = FILE_STATUS_TEMPORARY;
+  drupal_write_record('files', $file);
 
   $node = new stdClass();
   $node->nid = NULL;
@@ -1190,15 +1192,12 @@ function audio_api_insert($filepath, $ti
   $node->audio_tags = array();
   $node->audio_images = array();
 
-  $node->audio_file = array(
+  $node->audio = array(
     'newfile' => TRUE,
     'downloadable' => variable_get('audio_default_downloadable', 1),
     'play_count' => 0,
     'download_count' => 0,
-    'file_size' => filesize($filepath),
-    'file_name' => basename($filepath),
-    'file_path' => $filepath,
-    'file_mime' => module_exists('mimedetect') ? mimedetect_mime($filepath) : file_get_mimetype($filepath),
+    'file' => $file,
   );
 
   node_object_prepare($node);
@@ -1228,27 +1227,6 @@ function audio_api_insert($filepath, $ti
 }
 
 /**
- * Save the node's ID3 tags into the database.
- *
- * @param $node
- *   Node object.
- */
-function _audio_save_tags_to_db($node) {
-  $allowed_tags = audio_get_tags_allowed();
-
-  // Remove any existing metadata.
-  db_query("DELETE FROM {audio_metadata} WHERE vid=%d", $node->vid);
-
-  // Save the new tags.
-  foreach ($node->audio_tags as $tag => $value) {
-    if (in_array($tag, $allowed_tags) && $value) {
-      db_query("INSERT INTO {audio_metadata} (vid, tag, value, clean) VALUES (%d, '%s', '%s', '%s')",
-        $node->vid, $tag, $value, audio_clean_tag($value));
-    }
-  }
-}
-
-/**
  * Parse an array into a valid urlencoded query string.
  *
  * This function is a work-around for a drupal_urlencode issue in core.
@@ -1298,7 +1276,7 @@ function audio_query_string_encode($quer
  */
 function audio_is_flash_playable($node) {
   // Flash only supports a limited range of sample rates.
-  switch ($node->audio_file['sample_rate']) {
+  switch ($node->audio['sample_rate']) {
     case '44100': case '22050': case '11025':
       return TRUE;
     default:
@@ -1332,14 +1310,14 @@ function audio_token_values($type, $obje
     }
 
     // Formatted file info.
-    $tokens['audio-length'] = theme('audio_format_filelength', $node->audio_file);
-    $tokens['audio-format'] = theme('audio_format_fileformat', $node->audio_file);
+    $tokens['audio-length'] = theme('audio_format_filelength', $node->audio);
+    $tokens['audio-format'] = theme('audio_format_fileformat', $node->audio);
 
     // Raw file info.
-    $keys = array('file_format', 'file_name', 'file_path', 'file_mime', 'file_size', 'sample_rate', 'channel_mode', 'bitrate', 'bitrate_mode', 'playtime');
+    $keys = array('format', 'filename', 'filepath', 'filemime', 'file_size', 'sample_rate', 'channel_mode', 'bitrate', 'bitrate_mode', 'playtime');
     foreach ($keys as $key) {
-      if (isset($node->audio_file[$key])) {
-        $tokens['audio-'. strtr($key, '_', '-')] = check_plain($node->audio_file[$key]);
+      if (isset($node->audio[$key])) {
+        $tokens['audio-'. strtr($key, '_', '-')] = check_plain($node->audio[$key]);
       }
     }
     // Play and download links.
Index: audio.pages.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/audio/audio.pages.inc,v
retrieving revision 1.1
diff -u -p -r1.1 audio.pages.inc
--- audio.pages.inc	30 Sep 2008 05:00:52 -0000	1.1
+++ audio.pages.inc	1 Oct 2008 23:46:40 -0000
@@ -75,7 +75,7 @@ function audio_download($node) {
   // The mime_header_encode function does not (yet) support
   // quoted-string encoding of ASCII strings with special
   // characters.  See discussion at http://drupal.org/node/82614
-  $filename = $node->audio_file['file_name'];
+  $filename = $node->audio['file']->filename;
   // If the string contains non-ASCII characters, process it through
   // the mime_header_encode function.
   if (preg_match('/[^\x20-\x7E]/', $filename)) {
@@ -87,11 +87,11 @@ function audio_download($node) {
     $filename = '"'. str_replace('"', '\"', $filename) .'"';
   }
   $headers = array(
-    'Content-Type: '. mime_header_encode($node->audio_file['file_mime']),
-    'Content-Length: '. $node->audio_file['file_size'],
+    'Content-Type: '. mime_header_encode($node->audio['file']->filemime),
+    'Content-Length: '. $node->audio['file']->filesize,
     'Content-Disposition: attachment; filename='. $filename,
   );
-  audio_file_transfer($node->audio_file['file_path'], $headers);
+  audio_file_transfer($node->audio['file']->filepath, $headers);
 }
 
 /**
@@ -114,14 +114,14 @@ function audio_play($node = FALSE) {
     'Pragma: public',
     'Expires: 0',
     'Cache-Control: must-revalidate, post-check=0, pre-check=0, private',
-    'Content-Type: '. mime_header_encode($node->audio_file['file_mime']),
-    'Content-Length: '. $node->audio_file['file_size'],
+    'Content-Type: '. mime_header_encode($node->audio['file']->filemime),
+    'Content-Length: '. $node->audio['file']->filesize,
     'Content-Disposition: inline;',
     'Content-Transfer-Encoding: binary',
   );
   // Required for IE, otherwise Content-disposition is ignored.
   ini_set('zlib.output_compression', 'Off');
-  audio_file_transfer($node->audio_file['file_path'], $headers);
+  audio_file_transfer($node->audio['file']->filepath, $headers);
 }
 
 /**
Index: audio.theme.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/audio/audio.theme.inc,v
retrieving revision 1.1
diff -u -p -r1.1 audio.theme.inc
--- audio.theme.inc	25 May 2008 23:10:50 -0000	1.1
+++ audio.theme.inc	1 Oct 2008 23:46:40 -0000
@@ -23,8 +23,8 @@ function theme_audio_display($node) {
         . theme('audio_format_tag', $tag, $node->audio_tags[$tag], $setting);
     }
   }
-  $items[] = '<strong>'. t('Length') .':</strong> '. theme('audio_format_filelength', $node->audio_file);
-  $items[] = '<strong>'. t('Format') .':</strong> '. theme('audio_format_fileformat', $node->audio_file);
+  $items[] = '<strong>'. t('Length') .':</strong> '. theme('audio_format_filelength', $node->audio);
+  $items[] = '<strong>'. t('Format') .':</strong> '. theme('audio_format_fileformat', $node->audio);
 
   $output = "<div class='audio-node block'>\n";
   // give audio_image.module (or a theme) a chance to display the images.
@@ -63,10 +63,10 @@ function theme_audio_format_tag($tag, $v
  * Return a string describing the node's file size and play time.
  *
  * @param $fileinfo
- *   Audio node's audio_file array.
+ *   Audio node's audio array.
  */
 function theme_audio_format_filelength($fileinfo) {
-  $args['@filesize'] = format_size(isset($fileinfo['file_size']) ? $fileinfo['file_size'] : 0);
+  $args['@filesize'] = format_size(isset($fileinfo['file']->filesize) ? $fileinfo['file']->filesize : 0);
   if (empty($fileinfo['playtime'])) {
     return t('@filesize', $args);
   }
@@ -80,14 +80,14 @@ function theme_audio_format_filelength($
  * Return a string describing the node's file information.
  *
  * @param $fileinfo
- *   Audio node's audio_fileinfo array.
+ *   Audio node's audioinfo array.
  */
 function theme_audio_format_fileformat($fileinfo) {
   $format = '';
   $args = array();
-  if (!empty($fileinfo['file_format'])) {
-    $format .= '@file_format ';
-    $args['@file_format'] = check_plain(strtoupper($fileinfo['file_format']));
+  if (!empty($fileinfo['format'])) {
+    $format .= '@format ';
+    $args['@format'] = check_plain(strtoupper($fileinfo['format']));
   }
   if (!empty($fileinfo['channel_mode'])) {
     $format .= '@channel_mode ';
Index: audio_image.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/audio/audio_image.inc,v
retrieving revision 1.9
diff -u -p -r1.9 audio_image.inc
--- audio_image.inc	26 May 2008 17:30:40 -0000	1.9
+++ audio_image.inc	1 Oct 2008 23:46:40 -0000
@@ -78,42 +78,110 @@ function audio_image_type_dirty_array($i
 }
 
 /**
- * Creates an audio image array from a filepath and pic type.
+ * Creates a temporary audio image from a variable.
  *
  * The image is cropped to a square and then resized to the image size setting.
  *
- * @param $filepath
- *   Full path to the image file.
+ * @param $basename
+ *   Name of the audio file this image accompanies.
+ * @param $data
+ *   The raw image data.
+ * @param $mimetype
+ *   The MIME type of the image.
  * @param $pictype
  *   Integer pictype indexes from audio_image_type_clean_array() or
  *   audio_image_type_dirty_array().
  * @return
- *   An array with image info.
+ *   A file object with image info or FALSE on error.
  */
-function audio_image_from_file($filepath, $pictype) {
-  $size = variable_get('audio_image_size', 170);
+function audio_image_save_data($basename, $data, $mimetype, $pictype) {
+  global $user;
+
+  // Gotta have a name to save to.
+  $filepath = _audio_image_filename($basename, $mimetype, $pictype, TRUE);
+  if (!$filepath) {
+    return FALSE;
+  }
+
+  // Save the data.
+  $filepath = file_save_data($data, $filepath, FILE_EXISTS_RENAME);
+  if (!$filepath) {
+    return FALSE;
+  }
 
-  if ($image = image_get_info($filepath)) {
-    image_scale_and_crop($filepath, $filepath, $size, $size);
+  // Make sure it's a valid image.
+  $image = image_get_info($filepath);
+  if (!$image) {
+    file_delete($filepath);
+    return FALSE;
+  }
+
+  // Resize the image
+  $size = variable_get('audio_image_size', 170);
+  if (image_scale_and_crop($filepath, $filepath, $size, $size)) {
     // Changing the image dimensions will affect the file size. Clear out
     // PHP's cached value so we can find the new size.
     clearstatcache();
-
     $image = image_get_info($filepath);
-    return array(
-      'pictype'   => $pictype,
-      'filepath'  => $filepath,
-      'filemime'  => $image['mime_type'],
-      'filesize'  => $image['file_size'],
-      'extension' => $image['extension'],
-      'width'     => $image['width'],
-      'height'    => $image['height'],
-    );
   }
-  return FALSE;
+
+  // Store the file in the database so it can be removed by cron if it's not
+  // used.
+  $file = new stdClass();
+  $file->filepath = $filepath;
+  $file->filename = basename($file->filepath);
+  $file->filemime = $mimetype;
+  $file->filesize = $image['file_size'];
+  $file->uid = $user->uid;
+  $file->status = FILE_STATUS_TEMPORARY;
+  $file->timestamp = time();
+
+  drupal_write_record('files', $file);
+
+  $file->pictype = $pictype;
+  $file->height = $image['height'];
+  $file->width = $image['width'];
+
+  return $file;
 }
 
 /**
+ * If the file is an image it will be resized to meet the audio image size
+ * guidelines.
+ *
+ * @param $file
+ *   A Drupal file object. This function may resize the file affecting its size.
+ * @return
+ *   An array. If the file is an image and did not meet the requirements, it
+ *   will contain an error message.
+ */
+function audio_image_validate_size($file) {
+  $errors = array();
+
+  // Check first that the file is an image.
+  if ($info = image_get_info($file->filepath)) {
+    $size = variable_get('audio_image_size', 170);
+    if ($info['width'] > $size || $info['height'] > $size) {
+      // Try to resize the image to fit the dimensions.
+      if (image_get_toolkit() && image_scale_and_crop($file->filepath, $file->filepath, $size, $size)) {
+        drupal_set_message(t('The image was resized to fit within the maximum allowed dimensions of %height x %width pixels.', array('%height' => $size, '%width' => $size)));
+
+        // Clear the cached filesize and refresh the image information.
+        clearstatcache();
+        $info = image_get_info($file->filepath);
+        $file->filesize = $info['file_size'];
+      }
+      else {
+        $errors[] = t('The image is too large; the maximum dimensions are %dimensions pixels.', array('%dimensions' => $maximum_dimensions));
+      }
+    }
+  }
+
+  return $errors;
+}
+
+
+/**
  * Creates the image's filename in the form directory/prefix_imagetype.ext
  *
  * @param $prefix
@@ -122,13 +190,13 @@ function audio_image_from_file($filepath
  *   The image's mime type. jpeg, png and gif are the only formats allowed.
  * @param $pictype
  *   Integer specifying the picture type.
- * @param $in_tempdir 
+ * @param $in_tempdir
  *   Boolean indicating if the file be in the temp directory.
- * @return 
+ * @return
  *   Full filepath or null in case of an error.
  */
 function _audio_image_filename($prefix, $mimetype, $pictype = 0x03, $in_tempdir = FALSE) {
-  $directory = audio_get_directory() . (($in_tempdir) ? '/temp' : '/images');
+  $directory = $in_tempdir ? file_directory_temp() : audio_get_directory() . '/images';
   file_check_directory($directory, TRUE);
 
   //get the clean image type
Index: getid3/audio_getid3.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/audio/getid3/audio_getid3.module,v
retrieving revision 1.8
diff -u -p -r1.8 audio_getid3.module
--- getid3/audio_getid3.module	30 Sep 2008 18:43:15 -0000	1.8
+++ getid3/audio_getid3.module	1 Oct 2008 23:46:40 -0000
@@ -21,12 +21,12 @@ function audio_getid3_nodeapi(&$node, $o
 function audio_getid3_audio($op, &$node) {
   switch ($op) {
     case 'upload':
-      if ($info = audio_read_id3tags($node->audio_file['file_path'], TRUE)) {
+      if ($info = audio_read_id3tags($node->audio['file']->filepath, TRUE)) {
         $node->audio_tags = $info['tags'];
         $node->audio_images = $info['images'];
         // use array_merge so that the play count and downloadable settings aren't
         // overwritten.
-        $node->audio_file = array_merge($node->audio_file, $info['fileinfo']);
+        $node->audio = array_merge($node->audio, $info['fileinfo']);
       }
       break;
   }
@@ -37,54 +37,54 @@ function audio_getid3_audio($op, &$node)
  */
 function audio_getid3_form_alter(&$form, &$form_state, $form_id) {
   // We only alter audio node edit forms with a file attached.
-  if ($form_id == 'audio_node_form' && !empty($form['#node']->audio_file['file_path'])) {
+  if ($form_id == 'audio_node_form' && !empty($form['#node']->audio['file']->filepath)) {
     $form['#submit'][] = 'audio_getid3_node_submit';
-    $form['audio_file']['#description'] = t('This file information was loaded from the file by the getID3 library.');
+    $form['audio']['#description'] = t('This file information was loaded from the file by the getID3 library.');
 
     // Refresh the meta data everytime they display the edit form.
-    $info = audio_read_id3tags($form['#node']->audio_file['file_path'], FALSE);
+    $info = audio_read_id3tags($form['#node']->audio['file']->filepath, FALSE);
 
     // Overwrite the default audio module fields with a hidden value so that
     // so there's something POSTed back for the preview.
-    $fields = array('file_format', 'file_size', 'playtime', 'sample_rate', 'channel_mode', 'bitrate', 'bitrate_mode');
+    $fields = array('format', 'file_size', 'playtime', 'sample_rate', 'channel_mode', 'bitrate', 'bitrate_mode');
     foreach ($fields as $key) {
-      $form['audio_file'][$key] = array(
+      $form['audio'][$key] = array(
         '#type' => 'hidden',
         '#default_value' => isset($info['fileinfo'][$key]) ? $info['fileinfo'][$key] : '',
       );
     }
 
-    $form['audio_file']['display_file_format'] = array(
+    $form['audio']['display_format'] = array(
       '#type' => 'item',
       '#title' => t('Format'),
-      '#value' => $info['fileinfo']['file_format'],
+      '#value' => $info['fileinfo']['format'],
     );
-    $form['audio_file']['display_file_size'] = array(
+    $form['audio']['display_file_size'] = array(
       '#type' => 'item',
       '#title' => t('File Size'),
       '#value' => t('@filesize bytes', array('@filesize' => number_format($info['fileinfo']['file_size']))),
     );
-    $form['audio_file']['display_playtime'] = array(
+    $form['audio']['display_playtime'] = array(
       '#type' => 'item',
       '#title' => t('Length'),
       '#value' => $info['fileinfo']['playtime'],
     );
-    $form['audio_file']['display_sample_rate'] = array(
+    $form['audio']['display_sample_rate'] = array(
       '#type' => 'item',
       '#title' => t('Sample rate'),
       '#value' => t('@samplerate Hz', array('@samplerate' => number_format($info['fileinfo']['sample_rate']))),
     );
-    $form['audio_file']['display_channel_mode'] = array(
+    $form['audio']['display_channel_mode'] = array(
       '#type' => 'item',
       '#title' => t('Channel mode'),
       '#value' => ucfirst($info['fileinfo']['channel_mode']),
     );
-    $form['audio_file']['display_bitrate'] = array(
+    $form['audio']['display_bitrate'] = array(
       '#type' => 'item',
       '#title' => t('Bitrate'),
       '#value' => t('@bitrate bytes/second', array('@bitrate' => number_format($info['fileinfo']['bitrate']))),
     );
-    $form['audio_file']['display_bitrate_mode'] = array(
+    $form['audio']['display_bitrate_mode'] = array(
       '#type' => 'item',
       '#title' => t('Bitrate mode'),
       '#value' => strtoupper($info['fileinfo']['bitrate_mode']),
@@ -94,7 +94,7 @@ function audio_getid3_form_alter(&$form,
     // 11, 22, or 44 kHz). Display a warning if it is not.
     switch ($info['fileinfo']['sample_rate']) {
       case '44100': case '22050': case '11025':
-        if ($info['fileinfo']['file_format'] == 'mp3') {
+        if ($info['fileinfo']['format'] == 'mp3') {
           break;
         }
       default:
@@ -154,9 +154,9 @@ function audio_read_id3tags($filepath, $
     'tags' => array(),
     'images' => array(),
     'fileinfo' => array(
-      'file_format'  => $info['fileformat'],
+      'format'  => $info['fileformat'],
       'file_size'    => $info['filesize'],
-      'file_mime'    => $info['mime_type'],
+      'filemime'     => $info['mime_type'],
       'playtime'     => $info['playtime_string'],
       'bitrate'      => $info['audio']['bitrate'],
       'bitrate_mode' => $info['audio']['bitrate_mode'],
@@ -181,7 +181,8 @@ function audio_read_id3tags($filepath, $
       if (isset($info['id3v2'][$type])) {
         foreach ((array)$info['id3v2'][$type] as $image) {
           // Save it to a temp file
-          $ret['images'][] = audio_image_save_data(basename($filepath), $image['data'], $image['mime'], $image['picturetypeid']);
+          $file = audio_image_save_data(basename($filepath), $image['data'], $image['image_mime'], $image['picturetypeid']);
+          $ret['images'][$file->pictype .'-'. $file->fid] = $file;
         }
       }
     }
@@ -288,29 +289,37 @@ function audio_write_id3tags($filepath, 
 function _audio_getid3_save_to_file(&$node) {
   $settings = audio_get_tag_settings();
 
-  // prepare a list of tags to be written to the file
+  if (preg_match('/\.ogg$/i', $node->audio['file']->filepath)) {
+    $tagformats = array('vorbiscomment');
+  }
+  else {
+    $tagformats = array('id3v1', 'id3v2.3');
+  }
+
+  // Determine which tags should be written to the file.
   $tags = array();
-  foreach ($node->audio_tags as $tag => $value) {
-    if (isset($settings[$tag]) && $settings[$tag]['writetofile']) {
-      $tags[$tag] = $value;
+  if (isset($node->audio_tags)) {
+    foreach ($node->audio_tags as $tag => $value) {
+      if (isset($settings[$tag]) && $settings[$tag]['writetofile']) {
+        $tags[$tag] = $value;
+      }
     }
   }
 
-  // if there are any tags left, update the tags in the file
-  if ($tags) {
-    if (preg_match('/\.ogg$/i', $node->audio_file['file_path'])) {
-      $tagformats = array('vorbiscomment');
-    }
-    else {
-      $tagformats = array('id3v1', 'id3v2.3');
-    }
-    audio_write_id3tags($node->audio_file['file_path'], $tags, $node->audio_images, $tagformats);
+  $images = array();
+  if (isset($node->audio_images)) {
+    $images = $node->audio_images;
+  }
+
+  // If there are any writable tags or image, update the file.
+  if ($tags || $images) {
+    audio_write_id3tags($node->audio['file']->filepath, $tags, $images, $tagformats);
   }
 
   // then reload them so that the node is in sync with the file/database...
-  $info = audio_read_id3tags($node->audio_file['file_path']);
+  $info = audio_read_id3tags($node->audio['file']->filepath);
   // ...merge so that any non-written tags will be preserved...
   $node->audio_tags = array_merge($node->audio_tags, $info['tags']);
   // ...merge so that the playcount and downloadable options aren't overwritten.
-  $node->audio_file = array_merge($node->audio_file, $info['fileinfo']);
+  $node->audio = array_merge($node->audio, $info['fileinfo']);
 }
Index: images/audio_images.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/audio/images/audio_images.install,v
retrieving revision 1.1
diff -u -p -r1.1 audio_images.install
--- images/audio_images.install	26 May 2008 17:26:13 -0000	1.1
+++ images/audio_images.install	1 Oct 2008 23:46:41 -0000
@@ -19,17 +19,18 @@ function audio_images_schema() {
   $schema['audio_image'] = array(
     'description' => t('Associates an image (such as album artwork) with an audio file.'),
     'fields' => array(
-      'pid' => array(
-        'type' => 'serial',
-        'size' => 'medium',
+      'fid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
         'not null' => TRUE,
+        'description' => t('Primary Key: The {files}.fid.'),
       ),
-      'nid' => array(
+      'vid' => array(
         'type' => 'int',
         'size' => 'medium',
         'not null' => TRUE,
       ),
-      'vid' => array(
+      'nid' => array(
         'type' => 'int',
         'size' => 'medium',
         'not null' => TRUE,
@@ -52,28 +53,10 @@ function audio_images_schema() {
         'not null' => TRUE,
         'default' => 0,
       ),
-      'filemime' => array(
-        'type' => 'varchar',
-        'length' => 20,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'filepath' => array(
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'filesize' => array(
-        'type' => 'int',
-        'size' => 'medium',
-        'not null' => TRUE,
-        'default' => 0,
-      ),
     ),
-    'primary key' => array('pid'),
+    'primary key' => array('vid', 'pictype', 'fid'),
     'indexes' => array(
-      'audio_image_vid_pictype' => array('vid', 'pictype'),
+      'audio_image_fid' => array('fid'),
     ),
   );
   return $schema;
@@ -118,3 +101,37 @@ function audio_images_update_2() {
   );
   return $ret;
 }
+
+/**
+ * Move the majority of our data into the {files} table.
+ */
+function audio_images_update_6000() {
+  $ret = array();
+  $fid_field = array(
+    'type' => 'int',
+    'unsigned' => TRUE,
+    'not null' => TRUE,
+    'description' => t('Primary Key: The {files}.fid.'),
+  );
+  db_add_field($ret, 'audio_image', 'fid', $fid_field);
+  db_add_index($ret, 'audio_image', 'audio_image_fid', array('fid'));
+
+  // Load all the distinct filepaths.
+  $result = db_query("SELECT DISTINCT filepath FROM {audio_image} WHERE fid IS NULL OR fid = 0");
+  while ($file = db_fetch_object($result)) {
+    // Then move the data into the files table.
+    db_query("INSERT INTO {files} (uid, filename, filepath, filemime, filesize, status, timestamp) SELECT n.uid, '%s', ai.filepath, ai.filemime, ai.filesize, %d, n.created AS timestamp FROM {node} n INNER JOIN {audio_image} ai ON n.vid = ai.vid WHERE ai.filepath = '%s' LIMIT 1", array(basename($file->filepath), FILE_STATUS_PERMANENT, $file->filepath));
+    db_query("UPDATE {audio_image} SET fid = %d WHERE filepath = '%s'", db_last_insert_id('files', 'fid'), $file->filepath);
+  }
+
+  // Remove all the old fields and setup the new indexes.
+  db_drop_field($ret, 'audio_image', 'pid');
+  db_drop_field($ret, 'audio_image', 'filesize');
+  db_drop_field($ret, 'audio_image', 'filepath');
+  db_drop_field($ret, 'audio_image', 'filemime');
+  db_drop_index($ret, 'audio_image', 'audio_image_vid_pictype');
+  db_drop_primary_key($ret, 'audio_image');
+  db_add_primary_key($ret, 'audio_image', array('vid', 'pictype', 'fid'));
+
+  return $ret;
+}
Index: images/audio_images.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/audio/images/audio_images.module,v
retrieving revision 1.2
diff -u -p -r1.2 audio_images.module
--- images/audio_images.module	10 Jun 2008 05:23:28 -0000	1.2
+++ images/audio_images.module	1 Oct 2008 23:46:41 -0000
@@ -65,11 +65,9 @@ function audio_images_file_download($fil
  * Here we add our image fields to the audio node form.
  */
 function audio_images_form_alter(&$form, &$form_state, $form_id) {
-  // We only alter audio node edit forms.
-  if ($form_id == 'audio_node_form') {
-    $form['#submit'][] = 'audio_images_node_submit';
-
-    $node = $form['#node'];
+  // We only alter audio node edit forms with a file attached.
+  if ($form_id == 'audio_node_form' && !empty($form['#node']->audio['file']->filepath)) {
+    $form['#validate'][] = 'audio_images_node_form_validate';
 
     $form['audio_images'] = array(
       '#type' => 'fieldset', '#title' => t('Audio Images'),
@@ -79,24 +77,27 @@ function audio_images_form_alter(&$form,
       '#tree' => TRUE,
     );
 
-    if (isset($node->audio_images)) {
+    if (isset($form['#node']->audio_images)) {
       $form['audio_images']['#theme'] = 'audio_images_form';
-      foreach ($node->audio_images as $pid => $image) {
-        $form['audio_images'][$pid]['pid'] = array('#type' => 'value', '#value' => $pid);
-        $form['audio_images'][$pid]['pictype'] = array('#type' => 'value', '#value' => $image['pictype']);
-        $form['audio_images'][$pid]['filepath'] = array('#type' => 'value', '#value' => $image['filepath']);
-        $form['audio_images'][$pid]['filemime'] = array('#type' => 'value', '#value' => $image['filemime']);
-        $form['audio_images'][$pid]['filesize'] = array('#type' => 'value', '#value' => $image['filesize']);
-        $form['audio_images'][$pid]['height'] = array('#type' => 'value', '#value' => $image['height']);
-        $form['audio_images'][$pid]['width'] = array('#type' => 'value', '#value' => $image['width']);
-        $form['audio_images'][$pid]['delete'] = array('#type' => 'checkbox', '#default_value' => isset($image['delete']) ? $image['delete'] : FALSE);
+      foreach ($form['#node']->audio_images as $key => $image) {
+        if ($key == 'delete' || $key == 'new') continue;
+
+        $image = (array) $image;
+        $form['audio_images'][$key] = array(
+         '#type' => 'value',
+         '#value' => $image,
+        );
+        $form['audio_images']['delete'][$key] = array(
+          '#type' => 'checkbox',
+          '#default_value' => isset($image['delete']) ? $image['delete'] : FALSE,
+        );
       }
     }
 
     $form['audio_images']['new']['pictype'] = array(
       '#type' => 'select',
       '#title' => t('New image type'),
-      '#value' => variable_get('audio_image_default_type', 0x03),
+      '#default_value' => variable_get('audio_image_default_type', 0x03),
       '#options' => audio_image_type_dirty_array(),
     );
     $form['audio_images']['new']['audio_image_upload'] = array(
@@ -113,45 +114,48 @@ function audio_images_form_alter(&$form,
  */
 function theme_audio_images_form(&$form) {
   $pictypes = audio_image_type_dirty_array();
+  $header = array(t('Type'), t('MIME Type'), t('Dimensions'), t('Size'), t('Delete'));
   $rows = array();
-  foreach (element_children($form) as $pid) {
-    if ($pid != 'new') {
+  foreach (element_children($form) as $key) {
+    if ($key != 'new' && $key != 'delete') {
+      $image = (array) $form[$key]['#value'];
       $rows[] = array(
-        l($pictypes[$form[$pid]['pictype']['#value']], $form[$pid]['filepath']['#value']),
-        $form[$pid]['filemime']['#value'],
-        $form[$pid]['height']['#value'] .'x'. $form[$pid]['width']['#value'],
-        $form[$pid]['filesize']['#value'],
-        drupal_render($form[$pid]['delete']),
+        l($pictypes[$image['pictype']], $image['filepath']),
+        $image['filemime'],
+        format_size($image['filesize']),
+        $image['height'] .'x'. $image['width'],
+        drupal_render($form['delete'][$key]),
       );
     }
   }
-  $header = array(t('Type'), t('MIME Type'), t('Dimensions'), t('Size'), t('Delete'));
   return theme('table', $header, $rows) . drupal_render($form);
 }
 
 /**
- * Node submit handler
+ * Node validate handler
  */
-function audio_images_node_submit($form, &$form_state) {
-  $node = (object) $form_state['values'];
-
+function audio_images_node_form_validate($form, &$form_state) {
   // Check for an uploaded image.
-  $validators = array('file_validate_is_image' => array());
+  $validators = array(
+    'file_validate_is_image' => array(),
+    'audio_image_validate_size' => array(),
+  );
   if ($file = file_save_upload('audio_image_upload', $validators)) {
-    $pictype = (integer) $form_state['values']['audio_images']['new']['pictype'];
-    if ($image = audio_image_from_file($file->filepath, $pictype)) {
-      $pid = 'new_'. count($form_state['values']['audio_images']);
-      $image['pid'] = $pid;
-      $form_state['values']['audio_images'][$pid] = $image;
-    }
+    $image = image_get_info($file->filepath);
+    $file->height = $image['height'];
+    $file->width = $image['width'];
+    $file->pictype = (int) $form_state['values']['audio_images']['new']['pictype'];
+
+    $form_state['values']['audio_images'][$file->pictype .'-'. $file->fid] = $file;
   }
-  else {
-    foreach ($form_state['values']['audio_images'] as $key => $image) {
-      // If no image has been uploaded clear out the "new" image.
-      if (preg_match('/^new/', $key) && empty($image['filepath'])) {
-        unset($form_state['values']['audio_images'][$key]);
-      }
+  unset($form_state['values']['audio_images']['new']);
+
+  // Move the delete checkbox values to the image
+  if (isset($form_state['values']['audio_images']['delete'])) {
+    foreach ($form_state['values']['audio_images']['delete'] as $key => $value) {
+      $form_state['values']['audio_images'][$key]['delete'] = $value;
     }
+    unset($form_state['values']['audio_images']['delete']);
   }
 }
 
@@ -165,9 +169,6 @@ function audio_images_nodeapi(&$node, $o
     case 'insert':
       return audio_images_nodeapi_insert($node);
     case 'update':
-      if ($node->revision) {
-        return audio_images_nodeapi_insert_revision($node);
-      }
       return audio_images_nodeapi_update($node);
     case 'delete':
       return audio_images_nodeapi_delete($node);
@@ -178,133 +179,94 @@ function audio_images_nodeapi(&$node, $o
 
 function audio_images_nodeapi_load($node) {
   $ret['audio_images'] = array();
-  $result = db_query("SELECT pid, pictype, filemime, width, height, filepath, filesize FROM {audio_image} WHERE vid=%d", $node->vid);
-  while ($img = db_fetch_array($result)) {
-    $ret['audio_images'][$img['pid']] = $img;
+  $result = db_query("SELECT f.fid, f.filemime, f.filepath, f.filesize, ai.nid, ai.vid, ai.pictype, ai.width, ai.height FROM {files} f INNER JOIN {audio_image} ai ON f.fid = ai.fid WHERE ai.vid = %d", $node->vid);
+  while ($img = db_fetch_object($result)) {
+    $ret['audio_images'][$img->pictype .'-'. $img->fid] = $img;
   }
   return $ret;
 }
 
 function audio_images_nodeapi_insert(&$node) {
   // Add new images.
-  foreach ((array)$node->audio_images as $pid => $image) {
-    if (_audio_images_istemp($image['pid'])) {
-      _audio_images_save_new($node, $image);
-    }
-  }
-}
-
-function audio_images_nodeapi_insert_revision(&$node) {
-  foreach ((array)$node->audio_images as $pid => $image) {
-    // Deletions.
-    if ($image['delete']) {
-      _audio_images_delete($pid, $image['filepath']);
-    }
-    // Additions.
-    else if (_audio_images_istemp($image['pid'])) {
-      _audio_images_save_new($node, $image);
-    }
-    // Make copies of unchanged images.
-    else {
-      _audio_images_save_copy($node, $image);
+  foreach ((array) $node->audio_images as $key => $image) {
+    $image = (object) $image;
+    $image->nid = $node->nid;
+    $image->vid = $node->vid;
+
+    if ($image->status & FILE_STATUS_TEMPORARY == FILE_STATUS_TEMPORARY) {
+      $newpath = _audio_image_filename($node->vid, $image->filemime, $image->pictype, FALSE);
+      if (file_move($image, $newpath)) {
+        $image->status = FILE_STATUS_PERMANENT;
+        drupal_write_record('files', $file, array('fid'));
+      }
     }
+    drupal_write_record('audio_image', $image);
+    $node->audio_images[$key] = $image;
   }
 }
 
 function audio_images_nodeapi_update(&$node) {
-  foreach ((array)$node->audio_images as $pid => $image) {
-    // Deletions.
-    if ($image['delete']) {
-      _audio_images_delete($pid, $image['filepath']);
+  foreach ((array) $node->audio_images as $key => $image) {
+    $image = (object) $image;
+    $image->nid = $node->nid;
+    $image->vid = $node->vid;
+
+    if (!empty($image->delete)) {
+      // Delete the image.
+      _audio_images_delete($image);
+      db_query('DELETE FROM {audio_image} WHERE vid = %d AND pictype = %d AND fid = %d', $node->vid, $image->pictype, $image->fid);
+      unset($node->audio_images[$key]);
+    }
+    elseif (($image->status & FILE_STATUS_TEMPORARY) == FILE_STATUS_TEMPORARY) {
+      // New image.
+      $newpath = _audio_image_filename($node->vid, $image->filemime, $image->pictype, FALSE);
+      if (file_move($image, $newpath)) {
+        $image->status |= FILE_STATUS_PERMANENT;
+        drupal_write_record('files', $image, array('fid'));
+      }
+      drupal_write_record('audio_image', $image);
+      $node->audio_images[$key] = $image;
     }
-    // Additions.
-    else if (_audio_images_istemp($image['pid'])) {
-      _audio_images_save_new($node, $image);
+    elseif ($node->revision) {
+      // Make copies of unchanged images when creating a new revision.
+      drupal_write_record('audio_image', $image);
+      $node->audio_images[$key] = $image;
     }
   }
 }
 
 function audio_images_nodeapi_delete(&$node) {
-  // Delete any associated previews.
-  _audio_images_delete_previews($node);
-
   // Delete the image files and remove them from the database.
-  $result = db_query('SELECT filepath FROM {audio_image} WHERE nid = %d', $node->nid);
+  $result = db_query('SELECT ai.fid, f.filepath FROM {audio_image} ai INNER JOIN {files} f ON ai.fid = f.fid WHERE nid = %d', $node->nid);
   while ($file = db_fetch_object($result)) {
-    file_delete($file->filepath);
+    _audio_images_delete($file);
   }
   db_query('DELETE FROM {audio_image} WHERE nid = %d', $node->nid);
 }
 
 function audio_images_nodeapi_delete_revision(&$node) {
-  // Delete any associated previews.
-  _audio_images_delete_previews($node);
-
   // Delete the image files and remove them from the database.
-  $result = db_query('SELECT filepath FROM {audio_image} WHERE vid = %d', $node->vid);
+  $result = db_query('SELECT ai.fid, f.filepath FROM {audio_image} ai INNER JOIN {files} f ON ai.fid = f.fid WHERE vid = %d', $node->vid);
   while ($file = db_fetch_object($result)) {
-    file_delete($file->filepath);
+    _audio_images_delete($file);
   }
   db_query('DELETE FROM {audio_image} WHERE vid = %d', $node->vid);
 }
 
 /**
- * Is an image a temporary preview?
+ * If a file isn't used delete it and remove the {files} table record. The
+ * caller needs to remove record(s) from the {audio_images} table.
  *
- * @param $pid
- *   Mixed, string or integer.
- * @return
- *   Boolean indicating if the image is a temporary preview.
- */
-function _audio_images_istemp($pid) {
-  return (strpos($pid, 'new') !== FALSE);
-}
-
-/**
- * Adds an image to the audio_image table and moves it to the audio/images
- * directory.
+ * @param $file File object
  */
-function _audio_images_save_new(&$node, $image) {
-  $temppath = $image['filepath'];
-  _audio_images_save_copy($node, $image);
-  file_delete($temppath);
-}
-
-/**
- * Save an image to the audio_image table and copies it to the audio/images
- * directory.
- *
- * The caller needs to delete original image file if it was a temporary.
- */
-function _audio_images_save_copy(&$node, $image) {
-  $newpath = _audio_image_filename($node->vid, $image['filemime'], $image['pictype'], FALSE);
-  if (file_copy($image['filepath'], $newpath, FILE_EXISTS_REPLACE)) {
-    $image['nid'] = $node->nid;
-    $image['vid'] = $node->vid;
-    $image['filepath'] = $newpath;
-    $image['filesize'] = filesize($newpath);
-    drupal_write_record('audio_image', $image);
-    $node->audio_images[$image['pid']] = $image;
-  }
-}
-
-function _audio_images_delete($pid, $filepath) {
-  file_delete($filepath);
-  // Delete from the database if it's not a preview.
-  if (!_audio_images_istemp($pid)) {
-    db_query("DELETE FROM {audio_image} WHERE pid = %d", $pid);
-  }
-}
-
-/**
- * Delete any preview images associated with the node.
- */
-function _audio_images_delete_previews(&$node) {
-  foreach ((array)$node->audio_images as $pid => $image) {
-    if (_audio_images_istemp($pid)) {
-      file_delete($image['filepath']);
-      unset($node->audio_images[$pid]);
-    }
+function _audio_images_delete($file) {
+  // Check if the file will be used after this revision is deleted
+  $count = db_result(db_query('SELECT COUNT(fid) FROM {audio_image} WHERE fid = %d', $file->fid));
+
+  // If the file won't be used, delete it.
+  if ($count < 2) {
+    db_query('DELETE FROM {files} WHERE fid = %d', $file->fid);
+    file_delete($file->filepath);
   }
 }
 
@@ -317,9 +279,10 @@ function audio_images_get($audio_images,
   if (is_null($pictype)) {
     $pictype = variable_get('audio_default_image_type', 0x03);
   }
-  if (is_array($audio_images) && count($audio_images)) {
+  if (!empty($audio_images)) {
     foreach ($audio_images as $image) {
-      if ($image['pictype'] == $pictype) {
+      $image = (object) $image;
+      if ($image->pictype == $pictype) {
         return $image;
       }
     }
@@ -345,11 +308,10 @@ function theme_audio_images($audio_image
  * Create an <img> element for an audio image.
  */
 function theme_audio_image($image) {
-  $url = file_create_url($image['filepath']);
-  $alt = audio_image_type_dirty_array($image['pictype']);
+  $image = (object) $image;
 
-  list($width, $height) = @getimagesize($image['filepath']);
+  list($width, $height) = @getimagesize($image->filepath);
   $attributes = array('width' => $width, 'height' => $height);
 
-  return theme('image', $url, $alt, '', $attributes, FALSE);
+  return theme('image', file_create_url($image->filepath), audio_image_type_dirty_array($image->pictype), '', $attributes, FALSE);
 }
