? archive/Thumbs.db
? archive/scripts/1193091785.mp3
? catalog/The Database.mdb
? catalog/fixit.php
? catalog/import.php
? schedule/images/Thumbs.db
Index: station.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/station/station.install,v
retrieving revision 1.10
diff -u -p -u -p -r1.10 station.install
--- station.install	1 Nov 2007 05:50:32 -0000	1.10
+++ station.install	1 Nov 2007 05:54:23 -0000
@@ -9,8 +9,6 @@
 function station_uninstall() {
   variable_del('station_remote_archive_url');
   variable_del('station_remote_schedule_url');
-  variable_del('station_stream_high_url');
-  variable_del('station_stream_low_url');
 }
 
 
Index: station.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/station/station.module,v
retrieving revision 1.33
diff -u -p -u -p -r1.33 station.module
--- station.module	1 Nov 2007 05:50:32 -0000	1.33
+++ station.module	1 Nov 2007 17:28:01 -0000
@@ -217,6 +217,21 @@ function station_get_archive_url() {
 }
 
 /**
+ * If there's a schedule, return the URL.
+ *
+ * @return FALSE or string URL.
+ */
+function station_get_station_url($schedule_nid) {
+  if (module_exists('station_schedule')) {
+    return 'node/'. $schedule_nid;
+  }
+  else if ($url = variable_get('station_remote_schedule_url', FALSE)) {
+    return $url .'node/'. $schedule_nid;
+  }
+  return FALSE;
+}
+
+/**
  * Return a list of schedules.
  *
  * The list of schedules is cached between calls.
@@ -364,51 +379,53 @@ function station_get_program_at($timesta
 function station_block_current_program() {
   $schedule = station_default_schedule();
   $program = station_get_program_at(time(), $schedule['nid']);
-
-  $unscheduled_message = check_plain($schedule['unscheduled_message']);
-  $high = check_url(variable_get('station_stream_high_url', ''));
-  $low = check_url(variable_get('station_stream_low_url', ''));
-
-  return theme('station_block_current_program', $program, $unscheduled_message, $high, $low);
+  return theme('station_block_current_program', $schedule, $program);
 }
 
 /**
  * Theme the current program block.
  *
+ * @param $schedule
+ *   Schedule array returned by station_default_schedule().
  * @param $program
  *   Program node object.
- * @param $unscheduled_message
- *   message to display if no program is scheduled, i.e. when $program is null
- * @param $high_url
- *   optional, high-bandwidth webstream URL
- * @param $low_url
- *   optional, low-bandwith webstream URL
  * @return string
  */
-function theme_station_block_current_program($program = NULL, $unscheduled_message ='', $high_url = NULL, $low_url = NULL) {
-  // program or unscheduled...
+function theme_station_block_current_program($schedule, $program) {
+  // Program or unscheduled...
   if ($program) {
     $output = l($program->title, $program->node_url) .'<br />';
   }
   else {
-    $output = $unscheduled_message .'<br />';
+    $output = check_plain($schedule['unscheduled_message']) .'<br />';
   }
 
-  // webstream links
-  if ($high_url || $low_url) {
-    $output .= t('Tune in: ');
-    if ($high_url) {
-      $output .= ' '. l(t('High'), $high_url);
-    }
-    if ($low_url) {
-      $output .= ' '. l(t('Low'), $low_url);
-    }
-  }
+  // Streams
+  $output .= theme('station_streams', $schedule['nid'], $schedule['streams']);
 
   return $output;
 }
 
 /**
+ * Theme a schedule's web streams.
+ *
+ * @param $nid
+ *   A station schedule node.
+ * @param $streams
+ *   Schedule's streams.
+ */
+function theme_station_streams($schedule_nid, $streams) {
+  if (count($streams)) {
+    $output = t('Tune in: ');
+    foreach ($streams as $key => $stream) {
+      $output .= l($stream['name'], station_get_station_url($schedule_nid) ."/$key.m3u") .' ';
+    }
+    return $output;
+  }
+}
+
+
+/**
  * Convert an array to a comma separates list with an add between the last terms.
  *
  * @param $array array of items
Index: schedule/station_schedule.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/station/schedule/station_schedule.install,v
retrieving revision 1.15
diff -u -p -u -p -r1.15 station_schedule.install
--- schedule/station_schedule.install	1 Nov 2007 05:50:32 -0000	1.15
+++ schedule/station_schedule.install	1 Nov 2007 17:25:33 -0000
@@ -13,6 +13,7 @@ function station_schedule_install() {
         CREATE TABLE {station_schedule} (
           `nid` int unsigned NOT NULL default '0',
           `increment` int unsigned NOT NULL default '0',
+          `streams` longtext,
           `unscheduled_message` VARCHAR(255) NOT NULL default '',
           PRIMARY KEY(`nid`)
         ) /*!40100 DEFAULT CHARACTER SET utf8 */;
@@ -278,6 +279,7 @@ function station_schedule_update_5200() 
         CREATE TABLE {station_schedule} (
           `nid` int unsigned NOT NULL default '0',
           `increment` int unsigned NOT NULL default '0',
+          `streams` longtext,
           PRIMARY KEY(`nid`)
         ) /*!40100 DEFAULT CHARACTER SET utf8 */;
       ");
@@ -339,3 +341,39 @@ function station_schedule_update_5202() 
   }
   return $ret;
 }
+ 
+/**
+ * Move the streams from variables into the schedule table.
+ */
+function station_schedule_update_5203() {
+  $ret = array();
+  switch ($GLOBALS['db_type']) {
+    case 'mysql':
+    case 'mysqli':
+      $ret[] = update_sql("ALTER TABLE {station_schedule} ADD COLUMN `streams` longtext AFTER `increment`");
+
+      $streams = array();
+      if ($high = variable_get('station_stream_high_url', '')) {
+        $streams['high'] = array(
+           'name' => t('High'),
+           'description' => t('High bandwidth stream'),
+           'urls' => $high,
+        );
+      }
+      if ($low = variable_get('station_stream_low_url', '')) {
+        $streams['low'] = array(
+          'name' => t('Low'),
+          'description' => t('Low bandwidth stream'),
+          'urls' => $low,
+        );
+      }
+      if (count($streams)) {
+        db_query("UPDATE {station_schedule} SET streams = '%s', unscheduled_message = '%s'", serialize($streams), $unscheduled_message);
+      }
+      variable_del('station_stream_high_url');
+      variable_del('station_stream_low_url');
+
+      break;
+  }
+  return $ret;
+}
Index: schedule/station_schedule.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/station/schedule/station_schedule.module,v
retrieving revision 1.57
diff -u -p -u -p -r1.57 station_schedule.module
--- schedule/station_schedule.module	1 Nov 2007 05:50:32 -0000	1.57
+++ schedule/station_schedule.module	1 Nov 2007 17:29:26 -0000
@@ -67,6 +67,16 @@ function station_schedule_menu($may_cach
       $nid = (int) arg(1);
       $node = node_load($nid);
       if ($node->type == 'station_schedule') {
+        // Stream M3U links
+        foreach ($node->settings['streams'] as $key => $stream) {
+          $items[] = array(
+            'path' => "node/$nid/$key.m3u",
+            'type' => MENU_CALLBACK,
+            'callback' => 'station_schedule_stream_m3u',
+            'callback arguments' => array($stream),
+          );
+        }
+
         $items[] = array(
           'path' => "node/$nid/view/week",
           'title' => t('Week'),
@@ -183,8 +193,8 @@ function station_schedule_form($node) {
     '#type' => 'textfield',
     '#title' => check_plain($type->title_label),
     '#default_value' => $node->title,
-    '#required' =>  TRUE,
-    '#maxlength' =>  128,
+    '#required' => TRUE,
+    '#maxlength' => 128,
     '#description' => t("The name of the schedule, e.g. 'AM', 'FM', callsign."),
   );
 
@@ -201,9 +211,42 @@ function station_schedule_form($node) {
     '#title' => t('No scheduled programming message'),
     '#size' => 60,
     '#maxlength' => 255,
+    '#default_value' => isset($node->settings['unscheduled_message']) ? $node->settings['unscheduled_message'] : t("We're on autopilot."),
+    '#required' =>  TRUE,
     '#description' => t('This string will appear when no program is currently scheduled.'),
-    '#default_value' => $node->settings['unscheduled_message'],
   );
+  $form['settings']['streams'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Web streams'),
+    '#theme' => 'station_schedule_form_streams',
+    '#collapsible' => TRUE,
+    '#description' => t("If your station has webstreams enter them below."),
+  );
+  if (!isset($node->settings['streams']['new'])) {
+    $node->settings['streams']['new'] = array(
+      'name' => '',
+      'description' => '',
+      'urls' => array(),
+    );
+  }
+  foreach ($node->settings['streams'] as $key => $stream) {
+    $form['settings']['streams'][$key]['name'] = array(
+      '#type' => 'textfield',
+      '#size' => 10,
+      '#default_value' => $stream['name'],
+    );
+    $form['settings']['streams'][$key]['description'] = array(
+      '#type' => 'textfield',
+      '#size' => 20,
+      '#default_value' => $stream['description'],
+    );
+    $form['settings']['streams'][$key]['urls'] = array(
+      '#type' => 'textarea',
+      '#rows' => 2,
+      '#cols' => 20,
+      '#default_value' => implode("\n", $stream['urls']),
+    );
+  }
 
   if ($type->has_body) {
     $form['body_filter']['body'] = array(
@@ -220,6 +263,63 @@ function station_schedule_form($node) {
   return $form;
 }
 
+
+function theme_station_schedule_form_streams(&$form) {
+  $header = array(t('Name'), t('Description'), t('URLs'));
+  foreach (element_children($form) as $key) {
+    $row = array();
+    $row[] = drupal_render($form[$key]['name']);
+    $row[] = drupal_render($form[$key]['description']);
+    $row[] = drupal_render($form[$key]['urls']);
+    $rows[] = $row;
+  }
+  $output .= theme('table', $header, $rows);
+  $output .= drupal_render($form);
+
+  return $output;
+}
+
+/**
+ * Implementation of hook_validate().
+ */
+function station_schedule_validate(&$node) {
+  foreach ($node->settings['streams'] as $key => $stream) {
+    // Must have both a name and URL.
+    if (empty($stream['name']) xor empty($stream['urls'])) {
+      if (empty($stream['name'])) {
+        form_set_error("settings][streams][$key][name", t('You must provide a name for the webstream.'));
+      }
+      else {
+        form_set_error("settings][streams][$key][urls", t('You must provide a URL for the webstream.'));
+      }
+    }
+
+    foreach (explode("\n", $stream['urls']) as $url) {
+      $url = trim($url);
+      if (!empty($url) && !valid_url($url, TRUE)) {
+        form_set_error("settings][streams][$key][urls", t('An invalid webstream URL was provided: %url', array('%url' => $url)));
+      }
+    }
+  }
+}
+
+/**
+ * Implementation of hook_submit().
+ */
+function station_schedule_submit(&$node) {
+  $streams = array();
+  foreach ($node->settings['streams'] as $key => $stream) {
+    // Skip empty rows.
+    if (!empty($stream['name'])) {
+      // Convert URLs into an array.
+      $stream['urls'] = array_map('trim', explode("\n", $stream['urls']));
+      $streams[$stream['name']] = $stream;
+    }
+  }
+  $node->settings['streams'] = $streams;
+}
+
+
 /**
  * Implementation of hook_load().
  */
@@ -247,7 +347,13 @@ function station_schedule_load($node) {
   }
 
   // Load the settings.
-  $settings = db_fetch_array(db_query('SELECT increment, unscheduled_message FROM {station_schedule} WHERE nid = %d', $node->nid));
+  $settings = db_fetch_array(db_query('SELECT increment, streams, unscheduled_message FROM {station_schedule} WHERE nid = %d', $node->nid));
+  if (isset($settings['streams']) && $streams = unserialize($settings['streams'])) {
+    $settings['streams'] = $streams;
+  }
+  else {
+    $settings['streams'] = array();
+  }
 
   return array(
     'settings' => $settings,
@@ -259,7 +365,7 @@ function station_schedule_load($node) {
  * Implementation of hook_insert().
  */
 function station_schedule_insert($node) {
-  db_query("INSERT INTO {station_schedule} (nid, increment, unscheduled_message) VALUES (%d, %d, '%s')", $node->nid, $node->settings['increment'], $node->settings['unscheduled_message']);
+  db_query("INSERT INTO {station_schedule} (nid, increment, streams, unscheduled_message) VALUES (%d, %d, '%s', '%s')", $node->nid, $node->settings['increment'], serialize($node->settings['streams']), $node->settings['unscheduled_message']);
 }
 
 /**
@@ -275,7 +381,7 @@ function station_schedule_delete($node) 
  */
 function station_schedule_update($node) {
   db_query("DELETE FROM {station_schedule} WHERE nid = %d", $node->nid);
-  db_query("INSERT INTO {station_schedule} (nid, increment, unscheduled_message) VALUES (%d, %d, '%s')", $node->nid, $node->settings['increment'], $node->settings['unscheduled_message']);
+  db_query("INSERT INTO {station_schedule} (nid, increment, streams, unscheduled_message) VALUES (%d, %d, '%s', '%s')", $node->nid, $node->settings['increment'], serialize($node->settings['streams']), $node->settings['unscheduled_message']);
 }
 
 function station_schedule_admin_settings() {
@@ -736,9 +842,15 @@ function station_schedule_redirect_old($
  */
 function station_schedule_get_list() {
   $schedules = array();
-  $result = db_query("SELECT n.nid, n.title, ss.increment, ss.unscheduled_message FROM {node} n INNER JOIN {station_schedule} ss ON n.nid = ss.nid WHERE n.type = 'station_schedule'");
-  while ($o = db_fetch_array($result)) {
-    $schedules[$o['nid']] = $o;
+  $result = db_query("SELECT n.nid, n.title, ss.increment, ss.streams, ss.unscheduled_message FROM {node} n INNER JOIN {station_schedule} ss ON n.nid = ss.nid WHERE n.type = 'station_schedule'");
+  while ($schedule = db_fetch_array($result)) {
+    if (isset($schedule['streams']) && $streams = unserialize($schedule['streams'])) {
+      $schedule['streams'] = $streams;
+    }
+    else {
+      $schedule['streams'] = array();
+    }
+    $schedules[$schedule['nid']] = $schedule;
   }
 
   return $schedules;
@@ -793,7 +905,7 @@ function station_schedule_program_get_at
       return $node;
     }
   }
-  return new stdClass;
+  return new stdClass();
 }
 
 /**
@@ -1090,12 +1202,10 @@ function station_schedule_view($node, $t
 
   $node = node_prepare($node, $teaser);
   if ($teaser) {
-    $program = station_schedule_program_get_at(time(), $node->nid);
-    $node->content['current_program'] = array(
-      '#prefix' => t('On Air: '),
-      '#value' => isset($program) ? l($program->title, "node/$program->nid") : check_plain($node->settings['unscheduled_message']),
-      '#suffix' => ' ',
-      '#weight' => -6,
+    $node->content['streams'] = array(
+      '#title' => t('Webstreams'),
+      '#value' => theme('station_streams', $node->nid, $node->settings['streams']),
+      '#weight' => -5,
     );
   }
   else {
@@ -1223,3 +1333,19 @@ function station_schedule_day_page($node
 
   return $output;
 }
+
+/**
+ * Send the client an M3U file of the streams.
+ *
+ * @param $stream An array with a 'urls' item containing an array of webstream
+ *   URLs.
+ */
+function station_schedule_stream_m3u($stream) {
+  drupal_set_header('Content-Type: audio/mpegurl');
+  $output = '';
+  foreach ($stream['urls'] as $url) {
+    $output .= $url ."\n";
+  }
+  print $output;
+  exit();
+}
Index: schedule/views.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/station/schedule/views.inc,v
retrieving revision 1.13
diff -u -p -u -p -r1.13 views.inc
--- schedule/views.inc	31 Oct 2007 18:14:02 -0000	1.13
+++ schedule/views.inc	1 Nov 2007 17:22:55 -0000
@@ -27,6 +27,26 @@ function station_schedule_views_tables()
       ),
     ),
   );
+  $tables['station_schedule'] = array(
+    'name' => 'station_schedule',
+    'join' => array(
+      'left' => array(
+        'table' => 'node',
+        'field' => 'nid'
+      ),
+      'right' => array(
+        'field' => 'nid'
+      )
+    ),
+    'fields' => array(
+      'streams' => array(
+        'name' => t('Station Schedule: Web streams'),
+        'help' => t("Web stream links"),
+        'sortable' => FALSE,
+        'handler' => 'station_handler_field_schedule_streams',
+      ),
+    ),
+  );
   $tables['station_schedule_item_current'] = array(
     'name' => 'station_schedule_item',
     'join' => array(
@@ -146,6 +166,12 @@ function station_handler_field_schedule_
   }
 }
 
+function station_handler_field_schedule_streams($fieldinfo, $fielddata, $value, $data) {
+  if ($streams = unserialize($data->station_schedule_streams)) {
+    return theme('station_streams', $data->nid, $streams);
+  }
+}
+
 function station_handler_field_schedule_times($fieldinfo, $fielddata, $value, $data) {
   if ($program = node_load($data->nid)) {
     $scheduled = array();
