Index: README.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/emfield/README.txt,v
retrieving revision 1.1.6.6
diff -u -r1.1.6.6 README.txt
--- README.txt	12 Nov 2008 18:14:37 -0000	1.1.6.6
+++ README.txt	19 Dec 2008 20:22:50 -0000
@@ -17,6 +17,7 @@
 
 The module also allows field & provider specific settings and overrides, such as autoplay, resized thumbnails or videos for teasers, RSS support, and YouTube's 'related videos'. You can turn off individual provider support on a field or global basis.
 
+
 /***************/
  Refreshing Data
 /***************/
@@ -25,6 +26,7 @@
 
 If you have the Job Queue module enabled (from http://drupal.org/project/job_queue), you will be able to similarly refresh all nodes on your site, by visiting the 'Reload data' tab that will then appear on the Embedded Media Field configuration page, by browsing to Administer > Content management > Embedded Media Field configuration > Reload data (at /admin/content/emfield/reload). Then select the content type(s) you wish to refresh and press the Submit button. All nodes will then be refreshed on your next cron batch (or several crons if you have a lot of nodes on your site).
 
+
 /*********/
  Providers
 /*********/
@@ -65,6 +67,7 @@
     * Odeo
     * Podcast Alley
     * podOmatic
+    * Icecast streams
 
 You can:
 
@@ -72,4 +75,23 @@
     * Add embedded media fields to your content types at administer >> content >> types >> %YourType% >> add_field
     * Manage teaser and full node display settings at administer >> content >> types >> %YourType% >> fields
 
+
+/**************/
+ Audio: Icecast
+/**************/
+
+For security reasons, a Macromedia Flash movie playing in a web browser is not allowed to access data that resides outside the exact web domain from which the SWF originated. Using a crossdomain.xml policy file you can give the Flash Player permission to access data from a given domain without displaying a security dialog. When placed on a server, it tells the Flash Player to allow direct access to data on that server, without prompting the user grant access.
+
+In order to play Icecast streams you need to create a crossdomain.xml in the root of your domain. In it you must enter the remote domains that you wish to play icecast streams from. Unless the domain name is in crossdomain.xml, Icecast will not recognize it as an icecast stream. Below is an example of a crossdomain.xml file:
+
+<?xml version="1.0"?>
+<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
+<cross-domain-policy>
+	<allow-access-from domain="www.somewhere.com" />
+	<allow-access-from domain="*.anotherdomain.com" />
+</cross-domain-policy>
+
+For more information regarding the crossdomain.xml policy file, see: http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_14213
+
+
 For the most up-to-date documentation, please see http://drupal.org/node/184346
\ No newline at end of file
Index: contrib/emaudio/emaudio.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/emfield/contrib/emaudio/emaudio.install,v
retrieving revision 1.1.4.7
diff -u -r1.1.4.7 emaudio.install
--- contrib/emaudio/emaudio.install	9 Dec 2008 23:31:28 -0000	1.1.4.7
+++ contrib/emaudio/emaudio.install	19 Dec 2008 20:22:53 -0000
@@ -44,6 +44,14 @@
   return $ret;
 }
 
+function emaudio_update_6001() {
+  // Add a menu callback for emaudio/icecast/js.
+  menu_rebuild();
+
+  // Nothing to return, as we don't perform any manual queries.
+  return array();
+}
+
 // I'd like this to be conditional, so it only runs if they don't have views installed.
 // however, module_exists doesn't actually work in the update scripts.
 function _emaudio_update_reset_cache_views() {
Index: contrib/emaudio/providers/icecast.inc
===================================================================
RCS file: contrib/emaudio/providers/icecast.inc
diff -N contrib/emaudio/providers/icecast.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ contrib/emaudio/providers/icecast.inc	19 Dec 2008 20:22:53 -0000
@@ -0,0 +1,353 @@
+<?php
+// $Id: icecast.inc,v 1.1.4.5 2008/07/25 11:45:50 alexua Exp $
+
+/**
+ * @file
+ * This include processes Icecast audio files for use by emaudio.module.
+ */
+
+define('EMAUDIO_ICECAST_MAIN_URL', 'http://www.icecast.org/');
+define('EMAUDIO_ICECAST_CROSSDOMAINS_XML', 'crossdomain.xml');
+define('EMAUDIO_ICECAST_CROSSDOMAINS_ERR', 'Could not find a valid crossdomain.xml. Please create it (or fix it) and place it in the root of your drupal installation. Find more information about crossdomain.xml at <a href="http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_14213">adobe.com</a>.<br /> This file is required by <em>Embedded Audio Field</em> in order to play <em>Icecast</em> media streams.');
+
+/**
+ * hook emaudio_PROVIDER_info
+ * this returns information relevant to a specific 3rd party audio provider
+ * @return
+ *   an array of strings requested by various admin and other forms
+ *   'name' => the translated name of the provider
+ *   'url' => the url to the main page for the provider
+ *   'settings_description' => a description of the provider that will be posted in the admin settings form
+ *   'supported_features' => an array of rows describing the state of certain supported features by the provider.
+ *      These will be rendered in a table, with the columns being 'Feature', 'Supported', 'Notes'.
+ */
+function emaudio_icecast_info() {
+  $name = t('Icecast');
+  $features = array(
+    array(t('Autoplay'), t('Yes'), ''),
+    array(t('RSS Attachment'), t('No'), ''),
+    array(t('Thumbnails'), t('No'), t('')),
+  );
+  return array(
+    'provider' => 'icecast',
+    'name' => $name,
+    'url' => EMAUDIO_ICECAST_MAIN_URL,
+    'settings_description' => t('These settings specifically affect audio streams from !icecast.', array('!icecast' => l($name, EMAUDIO_ICECAST_MAIN_URL, array('target' => '_blank')))),
+    'supported_features' => $features,
+    'weight' => 9,
+  );
+}
+
+/**
+ * hook emaudio_PROVIDER_settings
+ * this should return a subform to be added to the emaudio_settings() admin settings page.
+ * note that a form field will already be provided, at $form['PROVIDER'] (such as $form['icecast'])
+ * so if you want specific provider settings within that field, you can add the elements to that form field.
+ */
+function emaudio_icecast_settings() {
+  if (!file_exists(EMAUDIO_ICECAST_CROSSDOMAINS_XML)) {
+    drupal_set_message(t(EMAUDIO_ICECAST_CROSSDOMAINS_ERR), 'error', FALSE);
+  }
+
+  $form = array();
+  return $form;
+}
+
+function emaudio_icecast_form(&$field, &$item) {
+  $form = array();
+  
+  // When previewing changes rather than saving them...
+  if (!isset($item['data']['icecast']) && isset($item['icecast']['icecast'])) {
+    $item['data']['icecast'] = $item['icecast']['icecast'];
+  }
+  
+  $form['icecast'] = array(
+    '#type' => 'fieldset',
+    '#description' => t('In order to update the currently playing song, the Icecast provider needs access to the admin section on the icecast server.'),
+    '#title' => t('Icecast'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE,
+  );
+  $form['icecast']['username'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Username'),
+    '#default_value' => $item['data']['icecast']['username'],
+    '#size' => 32,
+    '#maxlength' => 32,
+  );
+  $form['icecast']['password'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Password'),
+    '#default_value' => $item['data']['icecast']['password'],
+    '#size' => 32,
+    '#maxlength' => 32,
+  );
+  
+  return $form;
+}
+
+/**
+ * hook emaudio_PROVIDER_extract
+ * this is called to extract the video code from a pasted URL or embed code.
+ * @param $embed
+ *   an optional string with the pasted URL or embed code
+ * @return
+ *   either an array of regex expressions to be tested, or a string with the audio code to be used
+ *   if the hook tests the code itself, it should return either the string of the audio code (if matched), or an empty array.
+ *   otherwise, the calling function will handle testing the embed code against each regex string in the returned array.
+ */
+function emaudio_icecast_extract($embed = '') {
+  if (!$xml = @simplexml_load_file(EMAUDIO_ICECAST_CROSSDOMAINS_XML)) {
+    drupal_set_message(t(EMAUDIO_ICECAST_CROSSDOMAINS_ERR), 'error', FALSE);
+    return array();
+  }
+  
+  $domains = array();
+  $tag = 'allow-access-from';
+  foreach ($xml->$tag as $domain) {
+    $domains[] = (string) $domain['domain'];
+  }
+  $domains = str_replace('.', '\.', implode('|', $domains));
+  
+  return array(
+    '@http://((?:'. $domains .').*)@i',
+  );
+}
+
+/**
+ * hook emfield_PROVIDER_data
+ *
+ * provides an array to be serialised and made available with $item elsewhere
+ */
+function emaudio_icecast_data($field, $item) {
+  $data = array();
+  $data['icecast'] = $item['icecast']['icecast'];
+  return $data;
+}
+
+/**
+ * hook emaudio_PROVIDER_embedded_link($audio_code)
+ * returns a link to view the audio at the provider's site
+ *  @param $audio_code
+ *    the string containing the audio to watch
+ *  @return
+ *    a string containing the URL to view the audio at the original provider's site
+ */
+function emaudio_icecast_embedded_link($audio_code) {
+  return $audio_code;
+}
+
+/**
+ * hook emaudio_PROVIDER_thumbnail
+ * returns the external url for a thumbnail of a specific audio
+ * TODO: make the args: ($embed, $field, $item), with $field/$item provided if we need it, but otherwise simplifying things
+ *  @param $field
+ *    the field of the requesting node
+ *  @param $item
+ *    the actual content of the field from the requesting node
+ *  @return
+ *    a URL pointing to the thumbnail
+ */
+function emaudio_icecast_thumbnail($field, $item, $formatter, $node, $width, $height) {
+  return $tn;
+}
+
+/**
+ * hook emaudio_PROVIDER_audio
+ * this actually displays the full/normal-sized video we want, usually on the default page view
+ *  @param $embed
+ *    the video code for the audio to embed
+ *  @param $width
+ *    the width to display the audio
+ *  @param $height
+ *    the height to display the audio
+ *  @param $field
+ *    the field info from the requesting node
+ *  @param $item
+ *    the actual content from the field
+ *  @return
+ *    the html of the embedded audio
+ */
+function emaudio_icecast_audio($embed, $width, $height, $field, $item, &$node, $autoplay) {
+  $settings = array(
+    'emaudio_icecast' => array(
+      'fields' => array(),
+    ),
+  );
+  $settings['emaudioIcecast']['fields'][0] = implode('--', array('icecast', $node->nid, $field['field_name'], $item['#delta']));
+  
+  $output = theme('emaudio_icecast_flash_full', $embed, $width, $height, $autoplay, $field, $item, $settings);
+  return $output;
+}
+
+/**
+ * hook emaudio_PROVIDER_preview
+ * this actually displays the preview-sized video we want, commonly for the teaser
+ *  @param $embed
+ *    the video code for the audio to embed
+ *  @param $width
+ *    the width to display the audio
+ *  @param $height
+ *    the height to display the audio
+ *  @param $field
+ *    the field info from the requesting node
+ *  @param $item
+ *    the actual content from the field
+ *  @return
+ *    the html of the embedded audio
+ */
+function emaudio_icecast_preview($embed, $width, $height, $field, $item, &$node, $autoplay) {
+  $output = theme('emaudio_icecast_flash_preview', $embed, $width, $height, $autoplay);
+  return $output;
+}
+
+/**
+ * Implementation of hook_nodeapi()
+ */
+function emaudio_icecast_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
+  switch ($op) {
+    case 'validate':
+      // $form == $a3;
+      break;
+  }
+}
+
+/**
+ * Implementation of hook_emfield_subtheme.
+ */
+function emaudio_icecast_emfield_subtheme() {
+  return array(
+    'emaudio_icecast_flash_preview' => array(
+      'arguments' => array('embed' => NULL, 'width' => NULL, 'height' => NULL, 'autoplay' => FALSE),
+      'file' => 'providers/icecast.inc'
+    ),
+    'emaudio_icecast_flash_full' => array(
+      'arguments' => array('embed' => NULL, 'width' => NULL, 'height' => NULL, 'autoplay' => FALSE, 'field' => NULL, 'item' => NULL, 'settings' => NULL),
+      'file' => 'providers/icecast.inc'
+    ),
+    'emaudio_icecast_current_song' => array(
+      'arguments' => array('embed' => NULL, 'width' => NULL, 'height' => NULL, 'autoplay' => FALSE, 'field' => NULL, 'item' => NULL),
+      'file' => 'providers/icecast.inc'
+    )
+  );
+}
+
+/**
+ * the embedded flash displaying the podomatic audio
+ */
+function theme_emaudio_icecast_flash_preview($embed, $width, $height, $autoplay) {
+  $output = '';
+  if ($embed) {
+    $output .= swf('http://'. $embed, array('methods' => array('action' => SWFTOOLS_MP3_DISPLAY)));
+  }
+  return $output;
+}
+
+/**
+ * the embedded flash displaying the podomatic audio
+ */
+function theme_emaudio_icecast_flash_full($embed, $width, $height, $autoplay, $field, $item, $settings) {
+  $output = '';
+  if ($embed) {
+    // Fetch the currently playing song, and make sure the stream is live
+    $current_song = _emaudio_icecast_extract_current_song($embed, $item['data']['icecast']['username'], $item['data']['icecast']['password']);
+    if ($current_song !== FALSE) {
+      
+      $output .= swf('http://'. $embed, array('methods' => array('action' => SWFTOOLS_MP3_DISPLAY)));
+      $output .= '<div id="'. $settings['emaudioIcecast']['fields'][0] .'" class="emaudio-icecast-current-song"></div>'."\n";
+
+      drupal_add_js(drupal_get_path('module', 'emaudio') .'/providers/icecast.js');
+      drupal_add_js($settings, 'setting');
+    }
+  }
+  return $output;
+}
+
+/**
+ * Called by the AJAX script that updates the currently playing song
+ */
+function theme_emaudio_icecast_current_song($song) {
+  $output  = '<div class="field-label">'. t('Title') .'</div>';
+  $output .= '<div class="field-item icecast-current-song-title">'. $song['title'] .'</div>';
+  $output .= '<div class="field-label">'. t('Artist') .'</div>';
+  $output .= '<div class="field-item icecast-current-song-artist">'. $song['artist'] .'</div>';
+  return $output;
+}
+
+function _emaudio_icecast_extract_current_song($url, $username, $password) {
+  // Separate the icecast server root from the mountpoint
+  $lastslash = strrpos($url, '/');
+  if ($lastslash !== FALSE) {
+    $serverroot = substr($url, 0, $lastslash);
+    $mountpoint = substr($url, $lastslash + 1);
+  
+    // Now use that data to create the url of admin/stats.xml
+    $stats = 'http://'. $username .':'. $password .'@'. $serverroot .'/admin/stats.xml';
+    
+    // Fetch data for our mountpoint
+    $ch = curl_init($stats);
+    if ($ch !== FALSE) {
+      curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
+      curl_setopt($ch, CURLOPT_HEADER, FALSE);
+      $data = curl_exec($ch);
+      
+      if ($data !== FALSE) {
+        $xml = @simplexml_load_string($data);
+        if ($xml !== FALSE) {
+          $count = count($xml->source);
+          for ($i = 0; $i < $count; $i++) {
+            if ((string) $xml->source[$i]['mount'] == '/'. $mountpoint) {
+              $song = array(
+                'artist' => (string) $xml->source[$i]->artist[0],
+                'title' => (string) $xml->source[$i]->title[0],
+              );
+              
+              curl_close($ch);
+              return $song;
+            }
+          }
+        }
+      }
+      curl_close($ch);
+    }
+  }
+  
+  return FALSE;
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function emaudio_icecast_emfield_submenu() {
+  $items = array();
+  $items['emaudio/icecast/js'] = array(
+    'page callback' => 'emfield_include_invoke',
+    'page arguments' => array('emaudio', 'icecast', 'js'),
+    'access callback' => 'user_access',
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK,
+  );
+  return $items;
+}
+
+/**
+ * Menu-callback for JavaScript-based uploads.
+ */
+function emaudio_icecast_js($nid, $fieldname, $delta) {
+  $output = '&nbsp;';
+  
+  $node = node_load($nid);
+  if (!empty($node)) {
+    if (isset($node->{$fieldname}[$delta])) {
+      $stream   = $node->{$fieldname}[$delta]['value'];
+      $username = $node->{$fieldname}[$delta]['data']['icecast']['username'];
+      $password = $node->{$fieldname}[$delta]['data']['icecast']['password'];
+      
+      $song = _emaudio_icecast_extract_current_song($stream, $username, $password);
+      $output = theme('emaudio_icecast_current_song', $song);
+    }
+  }
+  
+  print drupal_to_js(array('status' => TRUE, 'data' => $output));
+  exit;
+}
Index: contrib/emaudio/providers/icecast.js
===================================================================
RCS file: contrib/emaudio/providers/icecast.js
diff -N contrib/emaudio/providers/icecast.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ contrib/emaudio/providers/icecast.js	19 Dec 2008 20:22:53 -0000
@@ -0,0 +1,22 @@
+// $Id$
+
+Drupal.icecast = Drupal.icecast || {};
+
+Drupal.behaviors.emaudioIcecast = function (context) {
+	for (var i = 0; i < Drupal.settings.emaudioIcecast['fields'].length; i++) {
+		Drupal.icecast.updateSong(Drupal.settings.emaudioIcecast['fields'][i]);
+		
+		setInterval("Drupal.icecast.updateSong('"+Drupal.settings.emaudioIcecast['fields'][i]+"')", 5000);
+	}
+};
+
+
+Drupal.icecast.updateSong = function(streamId) {
+	var streamDetails = streamId.split('--');
+	var $container = $('#' + streamId);
+	
+	$.get(Drupal.settings.basePath + 'emaudio/icecast/js/' + streamDetails[1] + '/' + streamDetails[2] + '/' + streamDetails[3], null, function(data){
+		var result = Drupal.parseJson(data);
+		$container.html(result['data']);
+	});		
+}