? test.patch
? 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: 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 05:59:28 -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 06:00:51 -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'),
@@ -204,6 +214,38 @@ function station_schedule_form($node) {
     '#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 +262,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 +346,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 +364,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 +380,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() {
@@ -1097,6 +1202,11 @@ function station_schedule_view($node, $t
       '#suffix' => ' ',
       '#weight' => -6,
     );
+    $node->content['streams'] = array(
+      '#title' => t('Webstreams'),
+      '#value' => theme('station_schedule_streams', $node),
+      '#weight' => -5,
+    );
   }
   else {
     $header[0] = array('data' => t('Time'));
@@ -1223,3 +1333,34 @@ 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();
+}
+
+/**
+ * Theme a station node's web streams.
+ *
+ * @param $node A station schedule node.
+ */
+function theme_station_schedule_streams($node) {
+  if (count($node->settings['streams'])) {
+    $output = t('Tune in: ');
+    foreach ($node->settings['streams'] as $key => $stream) {
+      $output .= l($stream['name'], "node/{$node->nid}/$key.m3u");
+    }
+    return $output;
+  }
+}
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 05:54:32 -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,13 @@ function station_handler_field_schedule_
   }
 }
 
+function station_handler_field_schedule_streams($fieldinfo, $fielddata, $value, $data) {
+  if ($streams = unserialize($data->station_schedule_streams)) {
+    $data->settings['streams'] = $streams;
+    return theme('station_schedule_streams', $data);
+  }
+}
+
 function station_handler_field_schedule_times($fieldinfo, $fielddata, $value, $data) {
   if ($program = node_load($data->nid)) {
     $scheduled = array();
Index: schedule/views_defaults.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/station/schedule/views_defaults.inc,v
retrieving revision 1.12
diff -u -p -u -p -r1.12 views_defaults.inc
--- schedule/views_defaults.inc	22 Aug 2007 17:23:30 -0000	1.12
+++ schedule/views_defaults.inc	1 Nov 2007 05:54:32 -0000
@@ -7,6 +7,79 @@
  */
 function station_schedule_views_default_views() {
   $view = new stdClass();
+  $view->name = 'station_schedule_now_playing';
+  $view->description = 'What\'s currently on the schedule(s)';
+  $view->access = array (
+);
+  $view->view_args_php = '';
+  $view->page = FALSE;
+  $view->page_title = 'On Air';
+  $view->page_header = '';
+  $view->page_header_format = '1';
+  $view->page_footer = '';
+  $view->page_footer_format = '1';
+  $view->page_empty = '';
+  $view->page_empty_format = '1';
+  $view->page_type = 'list';
+  $view->url = '';
+  $view->use_pager = TRUE;
+  $view->nodes_per_page = '10';
+  $view->block = TRUE;
+  $view->block_title = 'On Air';
+  $view->block_header = '';
+  $view->block_header_format = '1';
+  $view->block_footer = '';
+  $view->block_footer_format = '1';
+  $view->block_empty = '';
+  $view->block_empty_format = '1';
+  $view->block_type = 'teaser';
+  $view->nodes_per_block = '10';
+  $view->block_more = FALSE;
+  $view->block_use_page_header = FALSE;
+  $view->block_use_page_footer = FALSE;
+  $view->block_use_page_empty = FALSE;
+  $view->sort = array (
+  );
+  $view->argument = array (
+  );
+  $view->field = array (
+    array (
+      'tablename' => 'node',
+      'field' => 'title',
+      'label' => '',
+      'handler' => 'views_handler_field_nodelink',
+      'options' => 'link',
+    ),
+    array (
+      'tablename' => 'station_schedule_item_current',
+      'field' => 'program_nid',
+      'label' => '',
+    ),
+    array (
+      'tablename' => 'station_schedule',
+      'field' => 'streams',
+      'label' => '',
+    ),
+  );
+  $view->filter = array (
+    array (
+      'tablename' => 'node',
+      'field' => 'type',
+      'operator' => 'OR',
+      'options' => '',
+      'value' => array (
+  0 => 'station_schedule',
+),
+    ),
+  );
+  $view->exposed_filter = array (
+  );
+  $view->requires = array(node, station_schedule_item_current, station_schedule);
+  $views[$view->name] = $view;
+  
+  
+  
+  $view = new stdClass();
   $view->name = 'station_scheduled_programs';
   $view->description = 'Scheduled programs';
   $view->access = array (
