diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php
index 2ea6beb89..fbed8005d 100644
--- a/airtime_mvc/application/controllers/ApiController.php
+++ b/airtime_mvc/application/controllers/ApiController.php
@@ -27,6 +27,7 @@ class ApiController extends Zend_Controller_Action
                 ->addActionContext('live-chat', 'json')
                 ->addActionContext('update-file-system-mount', 'json')
                 ->addActionContext('handle-watched-dir-missing', 'json')
+                ->addActionContext('rabbitmq-do-push', 'json')
                 ->initContext();
     }
 
@@ -276,17 +277,19 @@ class ApiController extends Zend_Controller_Action
 
         $api_key = $this->_getParam('api_key');
 
+        /*
         if(!in_array($api_key, $CC_CONFIG["apiKey"]))
         {
             header('HTTP/1.0 401 Unauthorized');
             print 'You are not allowed to access this resource. ';
             exit;
         }
+        * */
 
         PEAR::setErrorHandling(PEAR_ERROR_RETURN);
 
-        $result = Application_Model_Schedule::GetScheduledPlaylists();
-        echo json_encode($result);
+        $data = Application_Model_Schedule::GetScheduledPlaylists();
+        echo json_encode($data, JSON_FORCE_OBJECT);
     }
 
     public function notifyMediaItemStartPlayAction()
@@ -316,6 +319,7 @@ class ApiController extends Zend_Controller_Action
         }
     }
 
+/*
     public function notifyScheduleGroupPlayAction()
     {
         global $CC_CONFIG;
@@ -355,6 +359,7 @@ class ApiController extends Zend_Controller_Action
             exit;
         }
     }
+    */
 
     public function recordedShowsAction()
     {
@@ -901,5 +906,26 @@ class ApiController extends Zend_Controller_Action
         $dir = base64_decode($request->getParam('dir'));
         Application_Model_MusicDir::removeWatchedDir($dir, false);
     }
+    
+    
+    /* This action is for use by our dev scripts, that make
+     * a change to the database and we want rabbitmq to send
+     * out a message to pypo that a potential change has been made. */
+    public function rabbitmqDoPushAction(){
+        global $CC_CONFIG;
+        
+        $request = $this->getRequest();
+        $api_key = $request->getParam('api_key');
+        if (!in_array($api_key, $CC_CONFIG["apiKey"]))
+        {
+            header('HTTP/1.0 401 Unauthorized');
+            print 'You are not allowed to access this resource.';
+            exit;
+        }
+        
+        Logging::log("Notifying RabbitMQ to send message to pypo");
+        
+        Application_Model_RabbitMq::PushSchedule();
+    }
 }
 
diff --git a/airtime_mvc/application/controllers/LibraryController.php b/airtime_mvc/application/controllers/LibraryController.php
index dd16c377c..64d411086 100644
--- a/airtime_mvc/application/controllers/LibraryController.php
+++ b/airtime_mvc/application/controllers/LibraryController.php
@@ -55,7 +55,9 @@ class LibraryController extends Zend_Controller_Action
         $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.FixedColumns.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
         $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.TableTools.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
 
+        $this->view->headScript()->appendFile($baseUrl.'/js/airtime/buttons/buttons.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
         $this->view->headScript()->appendFile($baseUrl.'/js/airtime/library/library.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
+        $this->view->headScript()->appendFile($baseUrl.'/js/airtime/library/main_library.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
 
         $this->view->headLink()->appendStylesheet($baseUrl.'/css/media_library.css?'.$CC_CONFIG['airtime_version']);
         $this->view->headLink()->appendStylesheet($baseUrl.'/css/jquery.contextMenu.css?'.$CC_CONFIG['airtime_version']);
diff --git a/airtime_mvc/application/controllers/PlaylistController.php b/airtime_mvc/application/controllers/PlaylistController.php
index b40de9d35..efa4fb8b4 100644
--- a/airtime_mvc/application/controllers/PlaylistController.php
+++ b/airtime_mvc/application/controllers/PlaylistController.php
@@ -197,7 +197,7 @@ class PlaylistController extends Zend_Controller_Action
 
     public function addItemsAction()
     {
-        $ids = $this->_getParam('ids');
+        $ids = $this->_getParam('ids', array());
         $ids = (!is_array($ids)) ? array($ids) : $ids;
     	$afterItem = $this->_getParam('afterItem', null);
     	$addType = $this->_getParam('type', 'after');
diff --git a/airtime_mvc/application/controllers/ScheduleController.php b/airtime_mvc/application/controllers/ScheduleController.php
index 86678e8ba..a97471759 100644
--- a/airtime_mvc/application/controllers/ScheduleController.php
+++ b/airtime_mvc/application/controllers/ScheduleController.php
@@ -59,6 +59,29 @@ class ScheduleController extends Zend_Controller_Action
         $this->view->headLink()->appendStylesheet($baseUrl.'/css/add-show.css?'.$CC_CONFIG['airtime_version']);
         $this->view->headLink()->appendStylesheet($baseUrl.'/css/jquery.contextMenu.css?'.$CC_CONFIG['airtime_version']);
 
+        //Start Show builder JS/CSS requirements
+        $this->view->headScript()->appendFile($baseUrl.'/js/contextmenu/jquery.contextMenu.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
+        $this->view->headScript()->appendFile($baseUrl.'/js/datatables/js/jquery.dataTables.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
+        $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.pluginAPI.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
+        $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.fnSetFilteringDelay.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
+        $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColVis.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
+        $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.ColReorder.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
+        $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.FixedColumns.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
+        $this->view->headScript()->appendFile($baseUrl.'/js/datatables/plugin/dataTables.TableTools.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
+
+        $this->view->headScript()->appendFile($baseUrl.'/js/airtime/buttons/buttons.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
+        $this->view->headScript()->appendFile($this->view->baseUrl('/js/airtime/library/events/library_showbuilder.js?'.$CC_CONFIG['airtime_version']),'text/javascript');
+        $this->view->headScript()->appendFile($baseUrl.'/js/airtime/library/library.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
+        $this->view->headScript()->appendFile($baseUrl.'/js/airtime/showbuilder/builder.js?'.$CC_CONFIG['airtime_version'],'text/javascript');
+
+        $this->view->headLink()->appendStylesheet($baseUrl.'/css/media_library.css?'.$CC_CONFIG['airtime_version']);
+        $this->view->headLink()->appendStylesheet($baseUrl.'/css/jquery.contextMenu.css?'.$CC_CONFIG['airtime_version']);
+        $this->view->headLink()->appendStylesheet($baseUrl.'/css/datatables/css/ColVis.css?'.$CC_CONFIG['airtime_version']);
+        $this->view->headLink()->appendStylesheet($baseUrl.'/css/datatables/css/ColReorder.css?'.$CC_CONFIG['airtime_version']);
+        $this->view->headLink()->appendStylesheet($baseUrl.'/css/TableTools.css?'.$CC_CONFIG['airtime_version']);
+        $this->view->headLink()->appendStylesheet($baseUrl.'/css/showbuilder.css?'.$CC_CONFIG['airtime_version']);
+        //End Show builder JS/CSS requirements
+
         Application_Model_Schedule::createNewFormSections($this->view);
 
         $userInfo = Zend_Auth::getInstance()->getStorage()->read();
@@ -78,10 +101,12 @@ class ScheduleController extends Zend_Controller_Action
 
 		$userInfo = Zend_Auth::getInstance()->getStorage()->read();
         $user = new Application_Model_User($userInfo->id);
-        if($user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER)))
+        if ($user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER))) {
             $editable = true;
-        else
+        }
+        else {
             $editable = false;
+        }
 
 		$this->view->events = Application_Model_Show::getFullCalendarEvents($start, $end, $editable);
     }
@@ -95,19 +120,19 @@ class ScheduleController extends Zend_Controller_Action
         $userInfo = Zend_Auth::getInstance()->getStorage()->read();
         $user = new Application_Model_User($userInfo->id);
 
-        if($user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER))) {
-            try{
+        if ($user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER))) {
+            try {
                 $showInstance = new Application_Model_ShowInstance($showInstanceId);
-            }catch(Exception $e){
+            } catch (Exception $e){
                 $this->view->show_error = true;
                 return false;
             }
             $error = $showInstance->moveShow($deltaDay, $deltaMin);
         }
 
-        if(isset($error))
+        if (isset($error)) {
             $this->view->error = $error;
-
+        }
     }
 
     public function resizeShowAction()
@@ -200,7 +225,7 @@ class ScheduleController extends Zend_Controller_Action
                 && !$instance->isRebroadcast()) {
 
                 $menu["schedule"] = array("name"=> "Add / Remove Content",
-                    "url" => "/showbuilder/index/");
+                    "url" => "/showbuilder/builder-dialog/");
 
                 $menu["clear"] = array("name"=> "Remove All Content", "icon" => "delete",
                     "url" => "/schedule/clear-show");
diff --git a/airtime_mvc/application/controllers/ShowbuilderController.php b/airtime_mvc/application/controllers/ShowbuilderController.php
index ef8177f7c..1a9082988 100644
--- a/airtime_mvc/application/controllers/ShowbuilderController.php
+++ b/airtime_mvc/application/controllers/ShowbuilderController.php
@@ -9,6 +9,7 @@ class ShowbuilderController extends Zend_Controller_Action
         $ajaxContext->addActionContext('schedule-move', 'json')
                     ->addActionContext('schedule-add', 'json')
                     ->addActionContext('schedule-remove', 'json')
+                    ->addActionContext('builder-dialog', 'json')
                     ->addActionContext('builder-feed', 'json')
                     ->initContext();
     }
@@ -53,11 +54,40 @@ class ShowbuilderController extends Zend_Controller_Action
         $this->view->headScript()->appendScript("var serverTimezoneOffset = {$offset}; //in seconds");
         $this->view->headScript()->appendFile($baseUrl.'/js/timepicker/jquery.ui.timepicker.js','text/javascript');
         $this->view->headScript()->appendFile($baseUrl.'/js/airtime/showbuilder/builder.js','text/javascript');
+        $this->view->headScript()->appendFile($baseUrl.'/js/airtime/showbuilder/main_builder.js','text/javascript');
 
         $this->view->headLink()->appendStylesheet($baseUrl.'/css/jquery.ui.timepicker.css');
         $this->view->headLink()->appendStylesheet($baseUrl.'/css/showbuilder.css');
     }
 
+    public function builderDialogAction() {
+
+        $request = $this->getRequest();
+        $id = $request->getParam("id");
+
+        $instance = CcShowInstancesQuery::create()->findPK($id);
+
+        if (is_null($instance)) {
+            $this->view->error = "show does not exist";
+            return;
+        }
+
+        $start = $instance->getDbStarts(null);
+        $start->setTimezone(new DateTimeZone(date_default_timezone_get()));
+        $end = $instance->getDbEnds(null);
+        $end->setTimezone(new DateTimeZone(date_default_timezone_get()));
+
+        $show_name = $instance->getCcShow()->getDbName();
+        $start_time = $start->format("Y-m-d H:i:s");
+        $end_time = $end->format("Y-m-d H:i:s");
+
+        $this->view->title = "{$show_name}:    {$start_time} - {$end_time}";
+        $this->view->start = $instance->getDbStarts("U");
+        $this->view->end = $instance->getDbEnds("U");
+
+        $this->view->dialog = $this->view->render('showbuilder/builderDialog.phtml');
+    }
+
     public function builderFeedAction() {
 
         $request = $this->getRequest();
diff --git a/airtime_mvc/application/forms/customfilters/ImageSize.php b/airtime_mvc/application/forms/customfilters/ImageSize.php
index 69db3cb82..17c40228e 100644
--- a/airtime_mvc/application/forms/customfilters/ImageSize.php
+++ b/airtime_mvc/application/forms/customfilters/ImageSize.php
@@ -6,7 +6,7 @@ class Zend_Filter_ImageSize implements Zend_Filter_Interface {
             throw new Zend_Filter_Exception('Image does not exist: ' . $value);
         }
 
-        $image = imageCreateFromString(file_get_contents($value));
+        $image = imagecreatefromstring(file_get_contents($value));
         if (false === $image) {
             throw new Zend_Filter_Exception('Can\'t load image: ' . $value);
         }
diff --git a/airtime_mvc/application/models/RabbitMq.php b/airtime_mvc/application/models/RabbitMq.php
index 4d0e501a5..a01d5c9a9 100644
--- a/airtime_mvc/application/models/RabbitMq.php
+++ b/airtime_mvc/application/models/RabbitMq.php
@@ -29,7 +29,7 @@ class Application_Model_RabbitMq
         $EXCHANGE = 'airtime-pypo';
         $channel->exchange_declare($EXCHANGE, 'direct', false, true);
 
-        $data = json_encode($md);
+        $data = json_encode($md, JSON_FORCE_OBJECT);
         $msg = new AMQPMessage($data, array('content_type' => 'text/plain'));
 
         $channel->basic_publish($msg, $EXCHANGE);
diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php
index 367278bc7..21306d43d 100644
--- a/airtime_mvc/application/models/Schedule.php
+++ b/airtime_mvc/application/models/Schedule.php
@@ -46,87 +46,6 @@ class Application_Model_Schedule {
     }
 
 
-    /**
-     * Returns array indexed by:
-     *    "playlistId"/"playlist_id" (aliases to the same thing)
-     *    "start"/"starts" (aliases to the same thing) as YYYY-MM-DD HH:MM:SS.nnnnnn
-     *    "end"/"ends" (aliases to the same thing) as YYYY-MM-DD HH:MM:SS.nnnnnn
-     *    "group_id"/"id" (aliases to the same thing)
-     *    "clip_length" (for audio clips this is the length of the audio clip,
-     *                   for playlists this is the length of the entire playlist)
-     *    "name" (playlist only)
-     *    "creator" (playlist only)
-     *    "file_id" (audioclip only)
-     *    "count" (number of items in the playlist, always 1 for audioclips.
-     *      Note that playlists with one item will also have count = 1.
-     *
-     * @param string $p_fromDateTime
-     *    In the format YYYY-MM-DD HH:MM:SS.nnnnnn
-     * @param string $p_toDateTime
-     *    In the format YYYY-MM-DD HH:MM:SS.nnnnnn
-     * @param boolean $p_playlistsOnly
-     *    Retrieve playlists as a single item.
-     * @return array
-     *      Returns empty array if nothing found
-     */
-
-    public static function GetItems($p_currentDateTime, $p_toDateTime, $p_playlistsOnly = true)
-    {
-        global $CC_CONFIG, $CC_DBC;
-        $rows = array();
-        if (!$p_playlistsOnly) {
-            $sql = "SELECT * FROM ".$CC_CONFIG["scheduleTable"]
-            ." WHERE (starts >= TIMESTAMP '$p_currentDateTime') "
-            ." AND (ends <= TIMESTAMP '$p_toDateTime')";
-            $rows = $CC_DBC->GetAll($sql);
-            foreach ($rows as &$row) {
-                $row["count"] = "1";
-                $row["playlistId"] = $row["playlist_id"];
-                $row["start"] = $row["starts"];
-                $row["end"] = $row["ends"];
-                $row["id"] = $row["group_id"];
-            }
-        } else {
-            $sql = "SELECT MIN(pt.creator) AS creator,"
-            ." st.group_id,"
-            ." SUM(st.clip_length) AS clip_length,"
-            ." MIN(st.file_id) AS file_id,"
-            ." COUNT(*) as count,"
-            ." MIN(st.playlist_id) AS playlist_id,"
-            ." MIN(st.starts) AS starts,"
-            ." MAX(st.ends) AS ends,"
-            ." MIN(sh.name) AS show_name,"
-            ." MIN(si.starts) AS show_start,"
-            ." MAX(si.ends) AS show_end"
-            ." FROM $CC_CONFIG[scheduleTable] as st"
-            ." LEFT JOIN $CC_CONFIG[playListTable] as pt"
-            ." ON st.playlist_id = pt.id"
-            ." LEFT JOIN $CC_CONFIG[showInstances] as si"
-            ." ON st.instance_id = si.id"
-            ." LEFT JOIN $CC_CONFIG[showTable] as sh"
-            ." ON si.show_id = sh.id"
-            //The next line ensures we only get songs that haven't ended yet
-            ." WHERE (st.ends >= TIMESTAMP '$p_currentDateTime')"
-            ." AND (st.ends <= TIMESTAMP '$p_toDateTime')"
-            //next line makes sure that we aren't returning items that
-            //are past the show's scheduled timeslot.
-            ." AND (st.starts < si.ends)"
-            ." GROUP BY st.group_id"
-            ." ORDER BY starts";
-
-            $rows = $CC_DBC->GetAll($sql);
-            if (!PEAR::isError($rows)) {
-                foreach ($rows as &$row) {
-                    $row["playlistId"] = $row["playlist_id"];
-                    $row["start"] = $row["starts"];
-                    $row["end"] = $row["ends"];
-                    $row["id"] = $row["group_id"];
-                }
-            }
-        }
-        return $rows;
-    }
-
     /**
      * Returns data related to the scheduled items.
      *
@@ -318,6 +237,7 @@ class Application_Model_Schedule {
         sched.starts AS sched_starts, sched.ends AS sched_ends, sched.id AS sched_id,
         sched.cue_in AS cue_in, sched.cue_out AS cue_out,
         sched.fade_in AS fade_in, sched.fade_out AS fade_out,
+        sched.status AS sched_status,
 
         ft.track_title AS file_track_title, ft.artist_name AS file_artist_name,
         ft.album_title AS file_album_title, ft.length AS file_length
@@ -476,6 +396,120 @@ class Application_Model_Schedule {
         return $diff;
     }
 
+    /**
+     * Returns array indexed by:
+     *    "playlistId"/"playlist_id" (aliases to the same thing)
+     *    "start"/"starts" (aliases to the same thing) as YYYY-MM-DD HH:MM:SS.nnnnnn
+     *    "end"/"ends" (aliases to the same thing) as YYYY-MM-DD HH:MM:SS.nnnnnn
+     *    "group_id"/"id" (aliases to the same thing)
+     *    "clip_length" (for audio clips this is the length of the audio clip,
+     *                   for playlists this is the length of the entire playlist)
+     *    "name" (playlist only)
+     *    "creator" (playlist only)
+     *    "file_id" (audioclip only)
+     *    "count" (number of items in the playlist, always 1 for audioclips.
+     *      Note that playlists with one item will also have count = 1.
+     *
+     * @param string $p_fromDateTime
+     *    In the format YYYY-MM-DD HH:MM:SS.nnnnnn
+     * @param string $p_toDateTime
+     *    In the format YYYY-MM-DD HH:MM:SS.nnnnnn
+     * @param boolean $p_playlistsOnly
+     *    Retrieve playlists as a single item.
+     * @return array
+     *      Returns null if nothing found
+     */
+    public static function GetItems($p_currentDateTime, $p_toDateTime) {
+        global $CC_CONFIG, $CC_DBC;
+        $rows = array();
+
+        $sql = "SELECT st.file_id AS file_id,"
+        ." st.id as id,"
+        ." st.starts AS start,"
+        ." st.ends AS end,"
+        ." st.cue_in AS cue_in,"
+        ." st.cue_out AS cue_out,"
+        ." st.fade_in AS fade_in,"
+        ." st.fade_out AS fade_out,"
+        ." si.starts as show_start,"
+        ." si.ends as show_end"
+        ." FROM $CC_CONFIG[scheduleTable] as st"
+        ." LEFT JOIN $CC_CONFIG[showInstances] as si"
+        ." ON st.instance_id = si.id"
+        ." ORDER BY start";
+
+        Logging::log($sql);
+
+        $rows = $CC_DBC->GetAll($sql);
+        if (PEAR::isError($rows)) {
+            return null;
+        }
+
+        return $rows;
+    }
+
+    public static function GetScheduledPlaylists($p_fromDateTime = null, $p_toDateTime = null){
+
+        global $CC_CONFIG, $CC_DBC;
+
+        /* if $p_fromDateTime and $p_toDateTime function parameters are null, then set range
+         * from "now" to "now + 24 hours". */
+        if (is_null($p_fromDateTime)) {
+            $t1 = new DateTime("@".time());
+            $range_start = $t1->format("Y-m-d H:i:s");
+        } else {
+            $range_start = Application_Model_Schedule::PypoTimeToAirtimeTime($p_fromDateTime);
+        }
+        if (is_null($p_fromDateTime)) {
+            $t2 = new DateTime("@".time());
+            $t2->add(new DateInterval("PT24H"));
+            $range_end = $t2->format("Y-m-d H:i:s");
+        } else {
+            $range_end = Application_Model_Schedule::PypoTimeToAirtimeTime($p_toDateTime);
+        }
+
+        // Scheduler wants everything in a playlist
+        $items = Application_Model_Schedule::GetItems($range_start, $range_end);
+
+        $data = array();
+        $utcTimeZone = new DateTimeZone("UTC");
+
+        $data["status"] = array();
+        $data["media"] = array();
+
+        foreach ($items as $item){
+
+            $storedFile = Application_Model_StoredFile::Recall($item["file_id"]);
+            $uri = $storedFile->getFileUrlUsingConfigAddress();
+
+            $showEndDateTime = new DateTime($item["show_end"], $utcTimeZone);
+            $trackEndDateTime = new DateTime($item["end"], $utcTimeZone);
+
+            /* Note: cue_out and end are always the same. */
+            /* TODO: Not all tracks will have "show_end" */
+
+            if ($trackEndDateTime->getTimestamp() > $showEndDateTime->getTimestamp()){
+                $diff = $trackEndDateTime->getTimestamp() - $showEndDateTime->getTimestamp();
+                //assuming ends takes cue_out into assumption
+                $item["cue_out"] = $item["cue_out"] - $diff;
+            }
+
+            $start = Application_Model_Schedule::AirtimeTimeToPypoTime($item["start"]);
+            $data["media"][$start] = array(
+                'id' => $storedFile->getGunid(),
+                'row_id' => $item["id"],
+                'uri' => $uri,
+                'fade_in' => Application_Model_Schedule::WallTimeToMillisecs($item["fade_in"]),
+                'fade_out' => Application_Model_Schedule::WallTimeToMillisecs($item["fade_out"]),
+                'cue_in' => Application_Model_DateHelper::CalculateLengthInSeconds($item["cue_in"]),
+                'cue_out' => Application_Model_DateHelper::CalculateLengthInSeconds($item["cue_out"]),
+                'start' => $start,
+                'end' => Application_Model_Schedule::AirtimeTimeToPypoTime($item["end"])
+            );
+        }
+
+        return $data;
+    }
 
     /**
      * Export the schedule in json formatted for pypo (the liquidsoap scheduler)
@@ -485,7 +519,7 @@ class Application_Model_Schedule {
      * @param string $p_toDateTime
      *      In the format "YYYY-MM-DD-HH-mm-SS"
      */
-    public static function GetScheduledPlaylists($p_fromDateTime = null, $p_toDateTime = null)
+    public static function GetScheduledPlaylistsOld($p_fromDateTime = null, $p_toDateTime = null)
     {
         global $CC_CONFIG, $CC_DBC;
 
@@ -546,7 +580,6 @@ class Application_Model_Schedule {
 
                 $starts = Application_Model_Schedule::AirtimeTimeToPypoTime($item["starts"]);
                 $medias[$starts] = array(
-                    'row_id' => $item["id"],
                     'id' => $storedFile->getGunid(),
                     'uri' => $uri,
                     'fade_in' => Application_Model_Schedule::WallTimeToMillisecs($item["fade_in"]),
@@ -554,7 +587,6 @@ class Application_Model_Schedule {
                     'fade_cross' => 0,
                     'cue_in' => Application_Model_DateHelper::CalculateLengthInSeconds($item["cue_in"]),
                     'cue_out' => Application_Model_DateHelper::CalculateLengthInSeconds($item["cue_out"]),
-                    'export_source' => 'scheduler',
                     'start' => $starts,
                     'end' => Application_Model_Schedule::AirtimeTimeToPypoTime($item["ends"])
                 );
diff --git a/airtime_mvc/application/models/Scheduler.php b/airtime_mvc/application/models/Scheduler.php
index 3df43b5ce..1c77774c9 100644
--- a/airtime_mvc/application/models/Scheduler.php
+++ b/airtime_mvc/application/models/Scheduler.php
@@ -229,6 +229,15 @@ class Application_Model_Scheduler {
                 }
             }
 
+            //update the status flag in cc_schedule.
+            $instances = CcShowInstancesQuery::create()
+                ->filterByPrimaryKeys($affectedShowInstances)
+                ->find($this->con);
+
+            foreach ($instances as $instance) {
+                $instance->updateScheduleStatus($this->con);
+            }
+
             //update the last scheduled timestamp.
             CcShowInstancesQuery::create()
                 ->filterByPrimaryKeys($affectedShowInstances)
@@ -383,11 +392,20 @@ class Application_Model_Scheduler {
                     }
                 }
 
-                foreach($showInstances as $instance) {
+                foreach ($showInstances as $instance) {
                     $this->removeGaps($instance);
                 }
             }
 
+            //update the status flag in cc_schedule.
+            $instances = CcShowInstancesQuery::create()
+                ->filterByPrimaryKeys($showInstances)
+                ->find($this->con);
+
+            foreach ($instances as $instance) {
+                $instance->updateScheduleStatus($this->con);
+            }
+
             //update the last scheduled timestamp.
             CcShowInstancesQuery::create()
                 ->filterByPrimaryKeys($showInstances)
diff --git a/airtime_mvc/application/models/Show.php b/airtime_mvc/application/models/Show.php
index ec4cdbcfe..6fa4fd132 100644
--- a/airtime_mvc/application/models/Show.php
+++ b/airtime_mvc/application/models/Show.php
@@ -125,10 +125,12 @@ class Application_Model_Show {
         }
 
         $hours = $deltaMin/60;
-        if($hours > 0)
+        if ($hours > 0) {
             $hours = floor($hours);
-        else
+        }
+        else {
             $hours = ceil($hours);
+        }
 
         $mins = abs($deltaMin%60);
 
@@ -149,6 +151,28 @@ class Application_Model_Show {
         //do both the queries at once.
         $CC_DBC->query($sql);
 
+        $con = Propel::getConnection(CcSchedulePeer::DATABASE_NAME);
+        $con->beginTransaction();
+
+        try {
+            //update the status flag in cc_schedule.
+            $instances = CcShowInstancesQuery::create()
+                ->filterByDbStarts($current_timestamp, Criteria::GREATER_EQUAL)
+                ->filterByDbShowId($this->_showId)
+                ->find($con);
+
+            foreach ($instances as $instance) {
+                $instance->updateScheduleStatus();
+            }
+
+            $con->commit();
+        }
+        catch (Exception $e) {
+            $con->rollback();
+            Logging::log("Couldn't update schedule status.");
+            Logging::log($e->getMessage());
+        }
+
         Application_Model_RabbitMq::PushSchedule();
     }
 
@@ -1043,6 +1067,33 @@ class Application_Model_Show {
             }
         }
 
+        if ($data['add_show_id'] != -1) {
+            $con = Propel::getConnection(CcSchedulePeer::DATABASE_NAME);
+            $con->beginTransaction();
+
+            //current timesamp in UTC.
+            $current_timestamp = gmdate("Y-m-d H:i:s");
+
+            try {
+                //update the status flag in cc_schedule.
+                $instances = CcShowInstancesQuery::create()
+                    ->filterByDbStarts($current_timestamp, Criteria::GREATER_EQUAL)
+                    ->filterByDbShowId($data['add_show_id'])
+                    ->find($con);
+
+                foreach ($instances as $instance) {
+                    $instance->updateScheduleStatus();
+                }
+
+                $con->commit();
+            }
+            catch (Exception $e) {
+                $con->rollback();
+                Logging::log("Couldn't update schedule status.");
+                Logging::log($e->getMessage());
+            }
+        }
+
         Application_Model_Show::populateShowUntil($showId);
         Application_Model_RabbitMq::PushSchedule();
         return $showId;
@@ -1491,7 +1542,7 @@ class Application_Model_Show {
         $events = array();
 
         $interval = $start->diff($end);
-        $days =  $interval->format('%a');
+        $days = $interval->format('%a');
 
         $shows = Application_Model_Show::getShows($start, $end);
 
@@ -1508,10 +1559,9 @@ class Application_Model_Show {
 
             if ($editable && (strtotime($today_timestamp) < strtotime($show["starts"]))) {
                 $options["editable"] = true;
-                $events[] = Application_Model_Show::makeFullCalendarEvent($show, $options);
-            } else {
-                $events[] = Application_Model_Show::makeFullCalendarEvent($show, $options);
             }
+
+            $events[] = Application_Model_Show::makeFullCalendarEvent($show, $options);
         }
 
         return $events;
@@ -1521,10 +1571,6 @@ class Application_Model_Show {
     {
         $event = array();
 
-        if($show["rebroadcast"]) {
-            $event["disableResizing"] = true;
-        }
-
         $startDateTime = new DateTime($show["starts"], new DateTimeZone("UTC"));
         $startDateTime->setTimezone(new DateTimeZone(date_default_timezone_get()));
 
@@ -1538,29 +1584,27 @@ class Application_Model_Show {
         $event["end"] = $endDateTime->format("Y-m-d H:i:s");
         $event["endUnix"] = $endDateTime->format("U");
         $event["allDay"] = false;
-        //$event["description"] = $show["description"];
         $event["showId"] = intval($show["show_id"]);
         $event["record"] = intval($show["record"]);
         $event["rebroadcast"] = intval($show["rebroadcast"]);
 
         // get soundcloud_id
-        if(!is_null($show["file_id"])){
+        if (!is_null($show["file_id"])){
             $file = Application_Model_StoredFile::Recall($show["file_id"]);
             $soundcloud_id = $file->getSoundCloudId();
-        }else{
-            $soundcloud_id = null;
         }
-        $event["soundcloud_id"] = (is_null($soundcloud_id) ? -1 : $soundcloud_id);
+
+        $event["soundcloud_id"] = isset($soundcloud_id) ? $soundcloud_id : -1;
 
         //event colouring
-        if($show["color"] != "") {
+        if ($show["color"] != "") {
             $event["textColor"] = "#".$show["color"];
         }
-        if($show["background_color"] != "") {
+        if ($show["background_color"] != "") {
             $event["color"] = "#".$show["background_color"];
         }
 
-        foreach($options as $key=>$value) {
+        foreach ($options as $key => $value) {
             $event[$key] = $value;
         }
 
diff --git a/airtime_mvc/application/models/ShowBuilder.php b/airtime_mvc/application/models/ShowBuilder.php
index e3804e17f..8762a1638 100644
--- a/airtime_mvc/application/models/ShowBuilder.php
+++ b/airtime_mvc/application/models/ShowBuilder.php
@@ -1,6 +1,7 @@
 <?php
 
 require_once 'formatters/LengthFormatter.php';
+require_once 'formatters/TimeFilledFormatter.php';
 
 class Application_Model_ShowBuilder {
 
@@ -30,7 +31,8 @@ class Application_Model_ShowBuilder {
         "cuein" => "",
         "cueout" => "",
         "fadein" => "",
-        "fadeout" => ""
+        "fadeout" => "",
+        "current" => false,
     );
 
     /*
@@ -47,37 +49,14 @@ class Application_Model_ShowBuilder {
         $this->epoch_now = time();
     }
 
-    private function formatTimeFilled($p_sec) {
-
-        $formatted = "";
-        $sign = ($p_sec < 0) ? "-" : "+";
-
-        $time = Application_Model_Playlist::secondsToPlaylistTime(abs($p_sec));
-        Logging::log("time is: ".$time);
-        $info = explode(":", $time);
-
-        $formatted .= $sign;
-
-        if (intval($info[0]) > 0) {
-            $info[0] = ltrim($info[0], "0");
-            $formatted .= " {$info[0]}h";
-        }
-
-        if (intval($info[1]) > 0) {
-            $info[1] = ltrim($info[1], "0");
-            $formatted .= " {$info[1]}m";
-        }
-
-        if (intval($info[2]) > 0) {
-            $sec = round($info[2], 0);
-            $formatted .= " {$sec}s";
-        }
-
-        return $formatted;
-    }
-
+    //check to see if this row should be editable.
     private function isAllowed($p_item, &$row) {
 
+        //cannot schedule in a recorded show.
+        if (intval($p_item["si_record"]) === 1) {
+            return;
+        }
+
         $showStartDT = new DateTime($p_item["si_starts"], new DateTimeZone("UTC"));
 
         //can only schedule the show if it hasn't started and you are allowed.
@@ -86,27 +65,10 @@ class Application_Model_ShowBuilder {
         }
     }
 
+    //information about whether a track is inside|boundary|outside a show.
     private function getItemStatus($p_item, &$row) {
 
-        $showEndDT = new DateTime($p_item["si_ends"]);
-        $schedStartDT = new DateTime($p_item["sched_starts"]);
-        $schedEndDT = new DateTime($p_item["sched_ends"]);
-
-        $showEndEpoch = intval($showEndDT->format("U"));
-        $schedStartEpoch = intval($schedStartDT->format("U"));
-        $schedEndEpoch = intval($schedEndDT->format("U"));
-
-        if ($schedEndEpoch < $showEndEpoch) {
-            $status = 0; //item will playout in full
-        }
-        else if ($schedStartEpoch < $showEndEpoch && $schedEndEpoch > $showEndEpoch) {
-            $status = 1; //item is on boundry
-        }
-        else {
-            $status = 2; //item is overscheduled won't play.
-        }
-
-        $row["status"] = $status;
+        $row["status"] = intval($p_item["sched_status"]);
     }
 
     private function getRowTimestamp($p_item, &$row) {
@@ -121,6 +83,16 @@ class Application_Model_ShowBuilder {
         $row["timestamp"] = $ts;
     }
 
+    private function isCurrent($p_epochItemStart, $p_epochItemEnd) {
+        $current = false;
+
+        if ($this->epoch_now >= $p_epochItemStart && $this->epoch_now < $p_epochItemEnd) {
+            $current = true;
+        }
+
+        return $current;
+    }
+
     private function makeHeaderRow($p_item) {
 
         $row = $this->defaultRowArray;
@@ -148,8 +120,8 @@ class Application_Model_ShowBuilder {
     private function makeScheduledItemRow($p_item) {
         $row = $this->defaultRowArray;
 
-        $this->isAllowed($p_item, $row);
         $this->getRowTimestamp($p_item, $row);
+        $this->isAllowed($p_item, $row);
 
         if (isset($p_item["sched_starts"])) {
 
@@ -157,9 +129,19 @@ class Application_Model_ShowBuilder {
             $schedStartDT->setTimezone(new DateTimeZone($this->timezone));
             $schedEndDT = new DateTime($p_item["sched_ends"], new DateTimeZone("UTC"));
             $schedEndDT->setTimezone(new DateTimeZone($this->timezone));
+            $showEndDT = new DateTime($p_item["si_ends"], new DateTimeZone("UTC"));
 
             $this->getItemStatus($p_item, $row);
 
+            $startsEpoch = intval($schedStartDT->format("U"));
+            $endsEpoch = intval($schedEndDT->format("U"));
+            $showEndEpoch = intval($showEndDT->format("U"));
+
+            //don't want an overbooked item to stay marked as current.
+            if ($this->isCurrent($startsEpoch, min($endsEpoch, $showEndEpoch))) {
+                $row["current"] = true;
+            }
+
             $row["id"] = intval($p_item["sched_id"]);
             $row["instance"] = intval($p_item["si_id"]);
             $row["starts"] = $schedStartDT->format("H:i:s");
@@ -179,7 +161,10 @@ class Application_Model_ShowBuilder {
 
             $this->contentDT = $schedEndDT;
         }
-        //show is empty
+        //show is empty or is a special kind of show (recording etc)
+        else if (intval($p_item["si_record"]) === 1) {
+            $row["record"] = true;
+        }
         else {
 
             $row["empty"] = true;
@@ -193,7 +178,6 @@ class Application_Model_ShowBuilder {
     private function makeFooterRow($p_item) {
 
         $row = $this->defaultRowArray;
-        $this->isAllowed($p_item, $row);
         $row["footer"] = true;
 
         $showEndDT = new DateTime($p_item["si_ends"], new DateTimeZone("UTC"));
@@ -201,7 +185,9 @@ class Application_Model_ShowBuilder {
 
         $runtime = bcsub($contentDT->format("U.u"), $showEndDT->format("U.u"), 6);
         $row["runtime"] = $runtime;
-        $row["fRuntime"] = $this->formatTimeFilled($runtime);
+
+        $timeFilled = new TimeFilledFormatter($runtime);
+        $row["fRuntime"] = $timeFilled->format();
 
         return $row;
     }
diff --git a/airtime_mvc/application/models/ShowInstance.php b/airtime_mvc/application/models/ShowInstance.php
index 8088b3a04..176bf9135 100644
--- a/airtime_mvc/application/models/ShowInstance.php
+++ b/airtime_mvc/application/models/ShowInstance.php
@@ -617,14 +617,14 @@ class Application_Model_ShowInstance {
     public function getTimeScheduledSecs()
     {
         $time_filled = $this->getTimeScheduled();
-        return Application_Model_Schedule::WallTimeToMillisecs($time_filled) / 1000;
+        return Application_Model_Playlist::playlistTimeToSeconds($time_filled);
     }
 
     public function getDurationSecs()
     {
         $ends = $this->getShowInstanceEnd(null);
         $starts = $this->getShowInstanceStart(null);
-        return $ends->format('U') - $starts->format('U');
+        return intval($ends->format('U')) - intval($starts->format('U'));
     }
 
     public function getPercentScheduled()
diff --git a/airtime_mvc/application/models/airtime/CcPlaylistcontents.php b/airtime_mvc/application/models/airtime/CcPlaylistcontents.php
index e533d9ada..441207585 100644
--- a/airtime_mvc/application/models/airtime/CcPlaylistcontents.php
+++ b/airtime_mvc/application/models/airtime/CcPlaylistcontents.php
@@ -35,39 +35,6 @@ class CcPlaylistcontents extends BaseCcPlaylistcontents {
        return parent::getDbFadeout($format);
     }
 
-    /**
-     * Just changing the default format to return subseconds
-     *
-     * @return     mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
-     * @throws     PropelException - if unable to parse/validate the date/time value.
-     */
-    public function getDbCuein($format = 'H:i:s.u')
-    {
-       return parent::getDbCuein($format);
-    }
-
-    /**
-     * Just changing the default format to return subseconds
-     *
-     * @return     mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
-     * @throws     PropelException - if unable to parse/validate the date/time value.
-     */
-    public function getDbCueout($format = 'H:i:s.u')
-    {
-       return parent::getDbCueout($format);
-    }
-
-    /**
-     * Just changing the default format to return subseconds
-     *
-     * @return     mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
-     * @throws     PropelException - if unable to parse/validate the date/time value.
-     */
-    public function getDbCliplength($format = 'H:i:s.u')
-    {
-       return parent::getDbCliplength($format);
-    }
-
     /**
      *
      * @param String in format SS.uuuuuu, Datetime, or DateTime accepted string.
@@ -124,88 +91,4 @@ class CcPlaylistcontents extends BaseCcPlaylistcontents {
         return $this;
     } // setDbFadeout()
 
-    /**
-     * Sets the value of [cuein] column to a normalized version of the date/time value specified.
-     *
-     * @param      mixed $v string, integer (timestamp), or DateTime value.  Empty string will
-     *                      be treated as NULL for temporal objects.
-     * @return     CcPlaylistcontents The current object (for fluent API support)
-     */
-    public function setDbCuein($v)
-    {
-        if ($v instanceof DateTime) {
-            $dt = $v;
-        }
-        else {
-            try {
-                $dt = new DateTime($v);
-            }
-            catch (Exception $x) {
-                throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
-            }
-        }
-
-        $this->cuein = $dt->format('H:i:s.u');
-        $this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEIN;
-
-        return $this;
-    } // setDbCuein()
-
-    /**
-     * Sets the value of [cueout] column to a normalized version of the date/time value specified.
-     *
-     * @param      mixed $v string, integer (timestamp), or DateTime value.  Empty string will
-     *                      be treated as NULL for temporal objects.
-     * @return     CcPlaylistcontents The current object (for fluent API support)
-     */
-    public function setDbCueout($v)
-    {
-        if ($v instanceof DateTime) {
-            $dt = $v;
-        }
-        else {
-            try {
-                $dt = new DateTime($v);
-            }
-            catch (Exception $x) {
-                throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
-            }
-        }
-
-        $this->cueout = $dt->format('H:i:s.u');
-        $this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEOUT;
-
-        return $this;
-    } // setDbCueout()
-
-    /**
-     * Sets the value of [cliplength] column to a normalized version of the date/time value specified.
-     *
-     * @param      mixed $v string, integer (timestamp), or DateTime value.  Empty string will
-     *                      be treated as NULL for temporal objects.
-     * @return     CcPlaylistcontents The current object (for fluent API support)
-     */
-    public function setDbCliplength($v)
-    {
-        if ($v instanceof DateTime) {
-            $dt = $v;
-        }
-        else {
-
-            try {
-
-                $dt = new DateTime($v);
-
-            } catch (Exception $x) {
-                throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
-            }
-        }
-
-        $this->cliplength = $dt->format('H:i:s.u');
-        $this->modifiedColumns[] = CcPlaylistcontentsPeer::CLIPLENGTH;
-
-        return $this;
-    } // setDbCliplength()
-
-
 } // CcPlaylistcontents
diff --git a/airtime_mvc/application/models/airtime/CcSchedule.php b/airtime_mvc/application/models/airtime/CcSchedule.php
index ff222d7a3..0a15b7a3c 100644
--- a/airtime_mvc/application/models/airtime/CcSchedule.php
+++ b/airtime_mvc/application/models/airtime/CcSchedule.php
@@ -15,11 +15,6 @@
  */
 class CcSchedule extends BaseCcSchedule {
 
-    public function getDbClipLength($format = 'H:i:s.u')
-    {
-        return parent::getDbClipLength($format);
-    }
-
     /**
      * Get the [optionally formatted] temporal [starts] column value.
      *
@@ -104,28 +99,6 @@ class CcSchedule extends BaseCcSchedule {
         return parent::getDbFadeout($format);
     }
 
-    /**
-     * Just changing the default format to return subseconds
-     *
-     * @return     mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
-     * @throws     PropelException - if unable to parse/validate the date/time value.
-     */
-    public function getDbCueIn($format = 'H:i:s.u')
-    {
-       return parent::getDbCuein($format);
-    }
-
-    /**
-     * Just changing the default format to return subseconds
-     *
-     * @return     mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
-     * @throws     PropelException - if unable to parse/validate the date/time value.
-     */
-    public function getDbCueOut($format = 'H:i:s.u')
-    {
-       return parent::getDbCueout($format);
-    }
-
     /**
      *
      * @param String in format SS.uuuuuu, Datetime, or DateTime accepted string.
@@ -182,89 +155,6 @@ class CcSchedule extends BaseCcSchedule {
         return $this;
     } // setDbFadeout()
 
-    /**
-     * Sets the value of [cuein] column to a normalized version of the date/time value specified.
-     *
-     * @param      mixed $v string, integer (timestamp), or DateTime value.  Empty string will
-     *                      be treated as NULL for temporal objects.
-     * @return     CcPlaylistcontents The current object (for fluent API support)
-     */
-    public function setDbCueIn($v)
-    {
-        if ($v instanceof DateTime) {
-            $dt = $v;
-        }
-        else {
-            try {
-                $dt = new DateTime($v);
-            }
-            catch (Exception $x) {
-                throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
-            }
-        }
-
-        $this->cue_in = $dt->format('H:i:s.u');
-        $this->modifiedColumns[] = CcSchedulePeer::CUE_IN;
-
-        return $this;
-    } // setDbCuein()
-
-    /**
-     * Sets the value of [cueout] column to a normalized version of the date/time value specified.
-     *
-     * @param      mixed $v string, integer (timestamp), or DateTime value.  Empty string will
-     *                      be treated as NULL for temporal objects.
-     * @return     CcPlaylistcontents The current object (for fluent API support)
-     */
-    public function setDbCueout($v)
-    {
-        if ($v instanceof DateTime) {
-            $dt = $v;
-        }
-        else {
-            try {
-                $dt = new DateTime($v);
-            }
-            catch (Exception $x) {
-                throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
-            }
-        }
-
-        $this->cue_out = $dt->format('H:i:s.u');
-        $this->modifiedColumns[] = CcSchedulePeer::CUE_OUT;
-
-        return $this;
-    } // setDbCueout()
-
-    /**
-     * Sets the value of [cliplength] column to a normalized version of the date/time value specified.
-     *
-     * @param      mixed $v string, integer (timestamp), or DateTime value.  Empty string will
-     *                      be treated as NULL for temporal objects.
-     * @return     CcPlaylistcontents The current object (for fluent API support)
-     */
-    public function setDbClipLength($v)
-    {
-        if ($v instanceof DateTime) {
-            $dt = $v;
-        }
-        else {
-
-            try {
-
-                $dt = new DateTime($v);
-
-            } catch (Exception $x) {
-                throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
-            }
-        }
-
-        $this->clip_length = $dt->format('H:i:s.u');
-        $this->modifiedColumns[] = CcSchedulePeer::CLIP_LENGTH;
-
-        return $this;
-    } // setDbCliplength()
-
     /**
      * Sets the value of [starts] column to a normalized version of the date/time value specified.
      *
diff --git a/airtime_mvc/application/models/airtime/CcShowInstances.php b/airtime_mvc/application/models/airtime/CcShowInstances.php
index d1c97f437..ecd34e10e 100644
--- a/airtime_mvc/application/models/airtime/CcShowInstances.php
+++ b/airtime_mvc/application/models/airtime/CcShowInstances.php
@@ -107,4 +107,33 @@ class CcShowInstances extends BaseCcShowInstances {
             return $dt->format($format);
         }
     }
+
+    //post save hook to update the cc_schedule status column for the tracks in the show.
+    public function updateScheduleStatus(PropelPDO $con) {
+
+        Logging::log("in post save for showinstances");
+
+        //scheduled track is in the show
+        CcScheduleQuery::create()
+            ->filterByDbInstanceId($this->id)
+            ->filterByDbEnds($this->ends, Criteria::LESS_EQUAL)
+            ->update(array('DbStatus' => 1), $con);
+
+        Logging::log("updating status for in show items.");
+
+        //scheduled track is a boundary track
+        CcScheduleQuery::create()
+            ->filterByDbInstanceId($this->id)
+            ->filterByDbStarts($this->ends, Criteria::LESS_THAN)
+            ->filterByDbEnds($this->ends, Criteria::GREATER_THAN)
+            ->update(array('DbStatus' => 2), $con);
+
+        //scheduled track is overbooked.
+        CcScheduleQuery::create()
+            ->filterByDbInstanceId($this->id)
+            ->filterByDbStarts($this->ends, Criteria::GREATER_THAN)
+            ->update(array('DbStatus' => 0), $con);
+
+    }
+
 } // CcShowInstances
diff --git a/airtime_mvc/application/models/airtime/map/CcPlaylistcontentsTableMap.php b/airtime_mvc/application/models/airtime/map/CcPlaylistcontentsTableMap.php
index d654fb799..c8a01608a 100644
--- a/airtime_mvc/application/models/airtime/map/CcPlaylistcontentsTableMap.php
+++ b/airtime_mvc/application/models/airtime/map/CcPlaylistcontentsTableMap.php
@@ -42,9 +42,9 @@ class CcPlaylistcontentsTableMap extends TableMap {
 		$this->addForeignKey('PLAYLIST_ID', 'DbPlaylistId', 'INTEGER', 'cc_playlist', 'ID', false, null, null);
 		$this->addForeignKey('FILE_ID', 'DbFileId', 'INTEGER', 'cc_files', 'ID', false, null, null);
 		$this->addColumn('POSITION', 'DbPosition', 'INTEGER', false, null, null);
-		$this->addColumn('CLIPLENGTH', 'DbCliplength', 'TIME', false, null, '00:00:00');
-		$this->addColumn('CUEIN', 'DbCuein', 'TIME', false, null, '00:00:00');
-		$this->addColumn('CUEOUT', 'DbCueout', 'TIME', false, null, '00:00:00');
+		$this->addColumn('CLIPLENGTH', 'DbCliplength', 'VARCHAR', false, null, '00:00:00');
+		$this->addColumn('CUEIN', 'DbCuein', 'VARCHAR', false, null, '00:00:00');
+		$this->addColumn('CUEOUT', 'DbCueout', 'VARCHAR', false, null, '00:00:00');
 		$this->addColumn('FADEIN', 'DbFadein', 'TIME', false, null, '00:00:00');
 		$this->addColumn('FADEOUT', 'DbFadeout', 'TIME', false, null, '00:00:00');
 		// validators
diff --git a/airtime_mvc/application/models/airtime/map/CcScheduleTableMap.php b/airtime_mvc/application/models/airtime/map/CcScheduleTableMap.php
index ebbe397c8..e2a988421 100644
--- a/airtime_mvc/application/models/airtime/map/CcScheduleTableMap.php
+++ b/airtime_mvc/application/models/airtime/map/CcScheduleTableMap.php
@@ -42,13 +42,14 @@ class CcScheduleTableMap extends TableMap {
 		$this->addColumn('STARTS', 'DbStarts', 'TIMESTAMP', true, null, null);
 		$this->addColumn('ENDS', 'DbEnds', 'TIMESTAMP', true, null, null);
 		$this->addForeignKey('FILE_ID', 'DbFileId', 'INTEGER', 'cc_files', 'ID', false, null, null);
-		$this->addColumn('CLIP_LENGTH', 'DbClipLength', 'TIME', false, null, '00:00:00');
+		$this->addColumn('CLIP_LENGTH', 'DbClipLength', 'VARCHAR', false, null, '00:00:00');
 		$this->addColumn('FADE_IN', 'DbFadeIn', 'TIME', false, null, '00:00:00');
 		$this->addColumn('FADE_OUT', 'DbFadeOut', 'TIME', false, null, '00:00:00');
-		$this->addColumn('CUE_IN', 'DbCueIn', 'TIME', false, null, '00:00:00');
-		$this->addColumn('CUE_OUT', 'DbCueOut', 'TIME', false, null, '00:00:00');
+		$this->addColumn('CUE_IN', 'DbCueIn', 'VARCHAR', false, null, '00:00:00');
+		$this->addColumn('CUE_OUT', 'DbCueOut', 'VARCHAR', false, null, '00:00:00');
 		$this->addColumn('MEDIA_ITEM_PLAYED', 'DbMediaItemPlayed', 'BOOLEAN', false, null, false);
 		$this->addForeignKey('INSTANCE_ID', 'DbInstanceId', 'INTEGER', 'cc_show_instances', 'ID', true, null, null);
+		$this->addColumn('STATUS', 'DbStatus', 'SMALLINT', true, null, 1);
 		// validators
 	} // initialize()
 
diff --git a/airtime_mvc/application/models/airtime/om/BaseCcPlaylistcontents.php b/airtime_mvc/application/models/airtime/om/BaseCcPlaylistcontents.php
index 457a9b958..8f823a25e 100644
--- a/airtime_mvc/application/models/airtime/om/BaseCcPlaylistcontents.php
+++ b/airtime_mvc/application/models/airtime/om/BaseCcPlaylistcontents.php
@@ -176,102 +176,33 @@ abstract class BaseCcPlaylistcontents extends BaseObject  implements Persistent
 	}
 
 	/**
-	 * Get the [optionally formatted] temporal [cliplength] column value.
+	 * Get the [cliplength] column value.
 	 * 
-	 *
-	 * @param      string $format The date/time format string (either date()-style or strftime()-style).
-	 *							If format is NULL, then the raw DateTime object will be returned.
-	 * @return     mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
-	 * @throws     PropelException - if unable to parse/validate the date/time value.
+	 * @return     string
 	 */
-	public function getDbCliplength($format = '%X')
+	public function getDbCliplength()
 	{
-		if ($this->cliplength === null) {
-			return null;
-		}
-
-
-
-		try {
-			$dt = new DateTime($this->cliplength);
-		} catch (Exception $x) {
-			throw new PropelException("Internally stored date/time/timestamp value could not be converted to DateTime: " . var_export($this->cliplength, true), $x);
-		}
-
-		if ($format === null) {
-			// Because propel.useDateTimeClass is TRUE, we return a DateTime object.
-			return $dt;
-		} elseif (strpos($format, '%') !== false) {
-			return strftime($format, $dt->format('U'));
-		} else {
-			return $dt->format($format);
-		}
+		return $this->cliplength;
 	}
 
 	/**
-	 * Get the [optionally formatted] temporal [cuein] column value.
+	 * Get the [cuein] column value.
 	 * 
-	 *
-	 * @param      string $format The date/time format string (either date()-style or strftime()-style).
-	 *							If format is NULL, then the raw DateTime object will be returned.
-	 * @return     mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
-	 * @throws     PropelException - if unable to parse/validate the date/time value.
+	 * @return     string
 	 */
-	public function getDbCuein($format = '%X')
+	public function getDbCuein()
 	{
-		if ($this->cuein === null) {
-			return null;
-		}
-
-
-
-		try {
-			$dt = new DateTime($this->cuein);
-		} catch (Exception $x) {
-			throw new PropelException("Internally stored date/time/timestamp value could not be converted to DateTime: " . var_export($this->cuein, true), $x);
-		}
-
-		if ($format === null) {
-			// Because propel.useDateTimeClass is TRUE, we return a DateTime object.
-			return $dt;
-		} elseif (strpos($format, '%') !== false) {
-			return strftime($format, $dt->format('U'));
-		} else {
-			return $dt->format($format);
-		}
+		return $this->cuein;
 	}
 
 	/**
-	 * Get the [optionally formatted] temporal [cueout] column value.
+	 * Get the [cueout] column value.
 	 * 
-	 *
-	 * @param      string $format The date/time format string (either date()-style or strftime()-style).
-	 *							If format is NULL, then the raw DateTime object will be returned.
-	 * @return     mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
-	 * @throws     PropelException - if unable to parse/validate the date/time value.
+	 * @return     string
 	 */
-	public function getDbCueout($format = '%X')
+	public function getDbCueout()
 	{
-		if ($this->cueout === null) {
-			return null;
-		}
-
-
-
-		try {
-			$dt = new DateTime($this->cueout);
-		} catch (Exception $x) {
-			throw new PropelException("Internally stored date/time/timestamp value could not be converted to DateTime: " . var_export($this->cueout, true), $x);
-		}
-
-		if ($format === null) {
-			// Because propel.useDateTimeClass is TRUE, we return a DateTime object.
-			return $dt;
-		} elseif (strpos($format, '%') !== false) {
-			return strftime($format, $dt->format('U'));
-		} else {
-			return $dt->format($format);
-		}
+		return $this->cueout;
 	}
 
 	/**
@@ -429,151 +360,61 @@ abstract class BaseCcPlaylistcontents extends BaseObject  implements Persistent
 	} // setDbPosition()
 
 	/**
-	 * Sets the value of [cliplength] column to a normalized version of the date/time value specified.
+	 * Set the value of [cliplength] column.
 	 * 
-	 * @param      mixed $v string, integer (timestamp), or DateTime value.  Empty string will
-	 *						be treated as NULL for temporal objects.
+	 * @param      string $v new value
 	 * @return     CcPlaylistcontents The current object (for fluent API support)
 	 */
 	public function setDbCliplength($v)
 	{
-		// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now')
-		// -- which is unexpected, to say the least.
-		if ($v === null || $v === '') {
-			$dt = null;
-		} elseif ($v instanceof DateTime) {
-			$dt = $v;
-		} else {
-			// some string/numeric value passed; we normalize that so that we can
-			// validate it.
-			try {
-				if (is_numeric($v)) { // if it's a unix timestamp
-					$dt = new DateTime('@'.$v, new DateTimeZone('UTC'));
-					// We have to explicitly specify and then change the time zone because of a
-					// DateTime bug: http://bugs.php.net/bug.php?id=43003
-					$dt->setTimeZone(new DateTimeZone(date_default_timezone_get()));
-				} else {
-					$dt = new DateTime($v);
-				}
-			} catch (Exception $x) {
-				throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
-			}
+		if ($v !== null) {
+			$v = (string) $v;
 		}
 
-		if ( $this->cliplength !== null || $dt !== null ) {
-			// (nested ifs are a little easier to read in this case)
-
-			$currNorm = ($this->cliplength !== null && $tmpDt = new DateTime($this->cliplength)) ? $tmpDt->format('H:i:s') : null;
-			$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
-
-			if ( ($currNorm !== $newNorm) // normalized values don't match 
-					|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
-					)
-			{
-				$this->cliplength = ($dt ? $dt->format('H:i:s') : null);
-				$this->modifiedColumns[] = CcPlaylistcontentsPeer::CLIPLENGTH;
-			}
-		} // if either are not null
+		if ($this->cliplength !== $v || $this->isNew()) {
+			$this->cliplength = $v;
+			$this->modifiedColumns[] = CcPlaylistcontentsPeer::CLIPLENGTH;
+		}
 
 		return $this;
 	} // setDbCliplength()
 
 	/**
-	 * Sets the value of [cuein] column to a normalized version of the date/time value specified.
+	 * Set the value of [cuein] column.
 	 * 
-	 * @param      mixed $v string, integer (timestamp), or DateTime value.  Empty string will
-	 *						be treated as NULL for temporal objects.
+	 * @param      string $v new value
 	 * @return     CcPlaylistcontents The current object (for fluent API support)
 	 */
 	public function setDbCuein($v)
 	{
-		// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now')
-		// -- which is unexpected, to say the least.
-		if ($v === null || $v === '') {
-			$dt = null;
-		} elseif ($v instanceof DateTime) {
-			$dt = $v;
-		} else {
-			// some string/numeric value passed; we normalize that so that we can
-			// validate it.
-			try {
-				if (is_numeric($v)) { // if it's a unix timestamp
-					$dt = new DateTime('@'.$v, new DateTimeZone('UTC'));
-					// We have to explicitly specify and then change the time zone because of a
-					// DateTime bug: http://bugs.php.net/bug.php?id=43003
-					$dt->setTimeZone(new DateTimeZone(date_default_timezone_get()));
-				} else {
-					$dt = new DateTime($v);
-				}
-			} catch (Exception $x) {
-				throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
-			}
+		if ($v !== null) {
+			$v = (string) $v;
 		}
 
-		if ( $this->cuein !== null || $dt !== null ) {
-			// (nested ifs are a little easier to read in this case)
-
-			$currNorm = ($this->cuein !== null && $tmpDt = new DateTime($this->cuein)) ? $tmpDt->format('H:i:s') : null;
-			$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
-
-			if ( ($currNorm !== $newNorm) // normalized values don't match 
-					|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
-					)
-			{
-				$this->cuein = ($dt ? $dt->format('H:i:s') : null);
-				$this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEIN;
-			}
-		} // if either are not null
+		if ($this->cuein !== $v || $this->isNew()) {
+			$this->cuein = $v;
+			$this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEIN;
+		}
 
 		return $this;
 	} // setDbCuein()
 
 	/**
-	 * Sets the value of [cueout] column to a normalized version of the date/time value specified.
+	 * Set the value of [cueout] column.
 	 * 
-	 * @param      mixed $v string, integer (timestamp), or DateTime value.  Empty string will
-	 *						be treated as NULL for temporal objects.
+	 * @param      string $v new value
 	 * @return     CcPlaylistcontents The current object (for fluent API support)
 	 */
 	public function setDbCueout($v)
 	{
-		// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now')
-		// -- which is unexpected, to say the least.
-		if ($v === null || $v === '') {
-			$dt = null;
-		} elseif ($v instanceof DateTime) {
-			$dt = $v;
-		} else {
-			// some string/numeric value passed; we normalize that so that we can
-			// validate it.
-			try {
-				if (is_numeric($v)) { // if it's a unix timestamp
-					$dt = new DateTime('@'.$v, new DateTimeZone('UTC'));
-					// We have to explicitly specify and then change the time zone because of a
-					// DateTime bug: http://bugs.php.net/bug.php?id=43003
-					$dt->setTimeZone(new DateTimeZone(date_default_timezone_get()));
-				} else {
-					$dt = new DateTime($v);
-				}
-			} catch (Exception $x) {
-				throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
-			}
+		if ($v !== null) {
+			$v = (string) $v;
 		}
 
-		if ( $this->cueout !== null || $dt !== null ) {
-			// (nested ifs are a little easier to read in this case)
-
-			$currNorm = ($this->cueout !== null && $tmpDt = new DateTime($this->cueout)) ? $tmpDt->format('H:i:s') : null;
-			$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
-
-			if ( ($currNorm !== $newNorm) // normalized values don't match 
-					|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
-					)
-			{
-				$this->cueout = ($dt ? $dt->format('H:i:s') : null);
-				$this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEOUT;
-			}
-		} // if either are not null
+		if ($this->cueout !== $v || $this->isNew()) {
+			$this->cueout = $v;
+			$this->modifiedColumns[] = CcPlaylistcontentsPeer::CUEOUT;
+		}
 
 		return $this;
 	} // setDbCueout()
diff --git a/airtime_mvc/application/models/airtime/om/BaseCcPlaylistcontentsQuery.php b/airtime_mvc/application/models/airtime/om/BaseCcPlaylistcontentsQuery.php
index 85262c7bd..8769dfe60 100644
--- a/airtime_mvc/application/models/airtime/om/BaseCcPlaylistcontentsQuery.php
+++ b/airtime_mvc/application/models/airtime/om/BaseCcPlaylistcontentsQuery.php
@@ -282,29 +282,20 @@ abstract class BaseCcPlaylistcontentsQuery extends ModelCriteria
 	/**
 	 * Filter the query on the cliplength column
 	 * 
-	 * @param     string|array $dbCliplength The value to use as filter.
-	 *            Accepts an associative array('min' => $minValue, 'max' => $maxValue)
+	 * @param     string $dbCliplength The value to use as filter.
+	 *            Accepts wildcards (* and % trigger a LIKE)
 	 * @param     string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
 	 *
 	 * @return    CcPlaylistcontentsQuery The current query, for fluid interface
 	 */
 	public function filterByDbCliplength($dbCliplength = null, $comparison = null)
 	{
-		if (is_array($dbCliplength)) {
-			$useMinMax = false;
-			if (isset($dbCliplength['min'])) {
-				$this->addUsingAlias(CcPlaylistcontentsPeer::CLIPLENGTH, $dbCliplength['min'], Criteria::GREATER_EQUAL);
-				$useMinMax = true;
-			}
-			if (isset($dbCliplength['max'])) {
-				$this->addUsingAlias(CcPlaylistcontentsPeer::CLIPLENGTH, $dbCliplength['max'], Criteria::LESS_EQUAL);
-				$useMinMax = true;
-			}
-			if ($useMinMax) {
-				return $this;
-			}
-			if (null === $comparison) {
+		if (null === $comparison) {
+			if (is_array($dbCliplength)) {
 				$comparison = Criteria::IN;
+			} elseif (preg_match('/[\%\*]/', $dbCliplength)) {
+				$dbCliplength = str_replace('*', '%', $dbCliplength);
+				$comparison = Criteria::LIKE;
 			}
 		}
 		return $this->addUsingAlias(CcPlaylistcontentsPeer::CLIPLENGTH, $dbCliplength, $comparison);
@@ -313,29 +304,20 @@ abstract class BaseCcPlaylistcontentsQuery extends ModelCriteria
 	/**
 	 * Filter the query on the cuein column
 	 * 
-	 * @param     string|array $dbCuein The value to use as filter.
-	 *            Accepts an associative array('min' => $minValue, 'max' => $maxValue)
+	 * @param     string $dbCuein The value to use as filter.
+	 *            Accepts wildcards (* and % trigger a LIKE)
 	 * @param     string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
 	 *
 	 * @return    CcPlaylistcontentsQuery The current query, for fluid interface
 	 */
 	public function filterByDbCuein($dbCuein = null, $comparison = null)
 	{
-		if (is_array($dbCuein)) {
-			$useMinMax = false;
-			if (isset($dbCuein['min'])) {
-				$this->addUsingAlias(CcPlaylistcontentsPeer::CUEIN, $dbCuein['min'], Criteria::GREATER_EQUAL);
-				$useMinMax = true;
-			}
-			if (isset($dbCuein['max'])) {
-				$this->addUsingAlias(CcPlaylistcontentsPeer::CUEIN, $dbCuein['max'], Criteria::LESS_EQUAL);
-				$useMinMax = true;
-			}
-			if ($useMinMax) {
-				return $this;
-			}
-			if (null === $comparison) {
+		if (null === $comparison) {
+			if (is_array($dbCuein)) {
 				$comparison = Criteria::IN;
+			} elseif (preg_match('/[\%\*]/', $dbCuein)) {
+				$dbCuein = str_replace('*', '%', $dbCuein);
+				$comparison = Criteria::LIKE;
 			}
 		}
 		return $this->addUsingAlias(CcPlaylistcontentsPeer::CUEIN, $dbCuein, $comparison);
@@ -344,29 +326,20 @@ abstract class BaseCcPlaylistcontentsQuery extends ModelCriteria
 	/**
 	 * Filter the query on the cueout column
 	 * 
-	 * @param     string|array $dbCueout The value to use as filter.
-	 *            Accepts an associative array('min' => $minValue, 'max' => $maxValue)
+	 * @param     string $dbCueout The value to use as filter.
+	 *            Accepts wildcards (* and % trigger a LIKE)
 	 * @param     string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
 	 *
 	 * @return    CcPlaylistcontentsQuery The current query, for fluid interface
 	 */
 	public function filterByDbCueout($dbCueout = null, $comparison = null)
 	{
-		if (is_array($dbCueout)) {
-			$useMinMax = false;
-			if (isset($dbCueout['min'])) {
-				$this->addUsingAlias(CcPlaylistcontentsPeer::CUEOUT, $dbCueout['min'], Criteria::GREATER_EQUAL);
-				$useMinMax = true;
-			}
-			if (isset($dbCueout['max'])) {
-				$this->addUsingAlias(CcPlaylistcontentsPeer::CUEOUT, $dbCueout['max'], Criteria::LESS_EQUAL);
-				$useMinMax = true;
-			}
-			if ($useMinMax) {
-				return $this;
-			}
-			if (null === $comparison) {
+		if (null === $comparison) {
+			if (is_array($dbCueout)) {
 				$comparison = Criteria::IN;
+			} elseif (preg_match('/[\%\*]/', $dbCueout)) {
+				$dbCueout = str_replace('*', '%', $dbCueout);
+				$comparison = Criteria::LIKE;
 			}
 		}
 		return $this->addUsingAlias(CcPlaylistcontentsPeer::CUEOUT, $dbCueout, $comparison);
diff --git a/airtime_mvc/application/models/airtime/om/BaseCcSchedule.php b/airtime_mvc/application/models/airtime/om/BaseCcSchedule.php
index 751babc2d..977298da2 100644
--- a/airtime_mvc/application/models/airtime/om/BaseCcSchedule.php
+++ b/airtime_mvc/application/models/airtime/om/BaseCcSchedule.php
@@ -96,6 +96,13 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 	 */
 	protected $instance_id;
 
+	/**
+	 * The value for the status field.
+	 * Note: this column has a database default value of: 1
+	 * @var        int
+	 */
+	protected $status;
+
 	/**
 	 * @var        CcShowInstances
 	 */
@@ -137,6 +144,7 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 		$this->cue_in = '00:00:00';
 		$this->cue_out = '00:00:00';
 		$this->media_item_played = false;
+		$this->status = 1;
 	}
 
 	/**
@@ -236,36 +244,13 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 	}
 
 	/**
-	 * Get the [optionally formatted] temporal [clip_length] column value.
+	 * Get the [clip_length] column value.
 	 * 
-	 *
-	 * @param      string $format The date/time format string (either date()-style or strftime()-style).
-	 *							If format is NULL, then the raw DateTime object will be returned.
-	 * @return     mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
-	 * @throws     PropelException - if unable to parse/validate the date/time value.
+	 * @return     string
 	 */
-	public function getDbClipLength($format = '%X')
+	public function getDbClipLength()
 	{
-		if ($this->clip_length === null) {
-			return null;
-		}
-
-
-
-		try {
-			$dt = new DateTime($this->clip_length);
-		} catch (Exception $x) {
-			throw new PropelException("Internally stored date/time/timestamp value could not be converted to DateTime: " . var_export($this->clip_length, true), $x);
-		}
-
-		if ($format === null) {
-			// Because propel.useDateTimeClass is TRUE, we return a DateTime object.
-			return $dt;
-		} elseif (strpos($format, '%') !== false) {
-			return strftime($format, $dt->format('U'));
-		} else {
-			return $dt->format($format);
-		}
+		return $this->clip_length;
 	}
 
 	/**
@@ -335,69 +320,23 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 	}
 
 	/**
-	 * Get the [optionally formatted] temporal [cue_in] column value.
+	 * Get the [cue_in] column value.
 	 * 
-	 *
-	 * @param      string $format The date/time format string (either date()-style or strftime()-style).
-	 *							If format is NULL, then the raw DateTime object will be returned.
-	 * @return     mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
-	 * @throws     PropelException - if unable to parse/validate the date/time value.
+	 * @return     string
 	 */
-	public function getDbCueIn($format = '%X')
+	public function getDbCueIn()
 	{
-		if ($this->cue_in === null) {
-			return null;
-		}
-
-
-
-		try {
-			$dt = new DateTime($this->cue_in);
-		} catch (Exception $x) {
-			throw new PropelException("Internally stored date/time/timestamp value could not be converted to DateTime: " . var_export($this->cue_in, true), $x);
-		}
-
-		if ($format === null) {
-			// Because propel.useDateTimeClass is TRUE, we return a DateTime object.
-			return $dt;
-		} elseif (strpos($format, '%') !== false) {
-			return strftime($format, $dt->format('U'));
-		} else {
-			return $dt->format($format);
-		}
+		return $this->cue_in;
 	}
 
 	/**
-	 * Get the [optionally formatted] temporal [cue_out] column value.
+	 * Get the [cue_out] column value.
 	 * 
-	 *
-	 * @param      string $format The date/time format string (either date()-style or strftime()-style).
-	 *							If format is NULL, then the raw DateTime object will be returned.
-	 * @return     mixed Formatted date/time value as string or DateTime object (if format is NULL), NULL if column is NULL
-	 * @throws     PropelException - if unable to parse/validate the date/time value.
+	 * @return     string
 	 */
-	public function getDbCueOut($format = '%X')
+	public function getDbCueOut()
 	{
-		if ($this->cue_out === null) {
-			return null;
-		}
-
-
-
-		try {
-			$dt = new DateTime($this->cue_out);
-		} catch (Exception $x) {
-			throw new PropelException("Internally stored date/time/timestamp value could not be converted to DateTime: " . var_export($this->cue_out, true), $x);
-		}
-
-		if ($format === null) {
-			// Because propel.useDateTimeClass is TRUE, we return a DateTime object.
-			return $dt;
-		} elseif (strpos($format, '%') !== false) {
-			return strftime($format, $dt->format('U'));
-		} else {
-			return $dt->format($format);
-		}
+		return $this->cue_out;
 	}
 
 	/**
@@ -420,6 +359,16 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 		return $this->instance_id;
 	}
 
+	/**
+	 * Get the [status] column value.
+	 * 
+	 * @return     int
+	 */
+	public function getDbStatus()
+	{
+		return $this->status;
+	}
+
 	/**
 	 * Set the value of [id] column.
 	 * 
@@ -563,51 +512,21 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 	} // setDbFileId()
 
 	/**
-	 * Sets the value of [clip_length] column to a normalized version of the date/time value specified.
+	 * Set the value of [clip_length] column.
 	 * 
-	 * @param      mixed $v string, integer (timestamp), or DateTime value.  Empty string will
-	 *						be treated as NULL for temporal objects.
+	 * @param      string $v new value
 	 * @return     CcSchedule The current object (for fluent API support)
 	 */
 	public function setDbClipLength($v)
 	{
-		// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now')
-		// -- which is unexpected, to say the least.
-		if ($v === null || $v === '') {
-			$dt = null;
-		} elseif ($v instanceof DateTime) {
-			$dt = $v;
-		} else {
-			// some string/numeric value passed; we normalize that so that we can
-			// validate it.
-			try {
-				if (is_numeric($v)) { // if it's a unix timestamp
-					$dt = new DateTime('@'.$v, new DateTimeZone('UTC'));
-					// We have to explicitly specify and then change the time zone because of a
-					// DateTime bug: http://bugs.php.net/bug.php?id=43003
-					$dt->setTimeZone(new DateTimeZone(date_default_timezone_get()));
-				} else {
-					$dt = new DateTime($v);
-				}
-			} catch (Exception $x) {
-				throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
-			}
+		if ($v !== null) {
+			$v = (string) $v;
 		}
 
-		if ( $this->clip_length !== null || $dt !== null ) {
-			// (nested ifs are a little easier to read in this case)
-
-			$currNorm = ($this->clip_length !== null && $tmpDt = new DateTime($this->clip_length)) ? $tmpDt->format('H:i:s') : null;
-			$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
-
-			if ( ($currNorm !== $newNorm) // normalized values don't match 
-					|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
-					)
-			{
-				$this->clip_length = ($dt ? $dt->format('H:i:s') : null);
-				$this->modifiedColumns[] = CcSchedulePeer::CLIP_LENGTH;
-			}
-		} // if either are not null
+		if ($this->clip_length !== $v || $this->isNew()) {
+			$this->clip_length = $v;
+			$this->modifiedColumns[] = CcSchedulePeer::CLIP_LENGTH;
+		}
 
 		return $this;
 	} // setDbClipLength()
@@ -713,101 +632,41 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 	} // setDbFadeOut()
 
 	/**
-	 * Sets the value of [cue_in] column to a normalized version of the date/time value specified.
+	 * Set the value of [cue_in] column.
 	 * 
-	 * @param      mixed $v string, integer (timestamp), or DateTime value.  Empty string will
-	 *						be treated as NULL for temporal objects.
+	 * @param      string $v new value
 	 * @return     CcSchedule The current object (for fluent API support)
 	 */
 	public function setDbCueIn($v)
 	{
-		// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now')
-		// -- which is unexpected, to say the least.
-		if ($v === null || $v === '') {
-			$dt = null;
-		} elseif ($v instanceof DateTime) {
-			$dt = $v;
-		} else {
-			// some string/numeric value passed; we normalize that so that we can
-			// validate it.
-			try {
-				if (is_numeric($v)) { // if it's a unix timestamp
-					$dt = new DateTime('@'.$v, new DateTimeZone('UTC'));
-					// We have to explicitly specify and then change the time zone because of a
-					// DateTime bug: http://bugs.php.net/bug.php?id=43003
-					$dt->setTimeZone(new DateTimeZone(date_default_timezone_get()));
-				} else {
-					$dt = new DateTime($v);
-				}
-			} catch (Exception $x) {
-				throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
-			}
+		if ($v !== null) {
+			$v = (string) $v;
 		}
 
-		if ( $this->cue_in !== null || $dt !== null ) {
-			// (nested ifs are a little easier to read in this case)
-
-			$currNorm = ($this->cue_in !== null && $tmpDt = new DateTime($this->cue_in)) ? $tmpDt->format('H:i:s') : null;
-			$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
-
-			if ( ($currNorm !== $newNorm) // normalized values don't match 
-					|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
-					)
-			{
-				$this->cue_in = ($dt ? $dt->format('H:i:s') : null);
-				$this->modifiedColumns[] = CcSchedulePeer::CUE_IN;
-			}
-		} // if either are not null
+		if ($this->cue_in !== $v || $this->isNew()) {
+			$this->cue_in = $v;
+			$this->modifiedColumns[] = CcSchedulePeer::CUE_IN;
+		}
 
 		return $this;
 	} // setDbCueIn()
 
 	/**
-	 * Sets the value of [cue_out] column to a normalized version of the date/time value specified.
+	 * Set the value of [cue_out] column.
 	 * 
-	 * @param      mixed $v string, integer (timestamp), or DateTime value.  Empty string will
-	 *						be treated as NULL for temporal objects.
+	 * @param      string $v new value
 	 * @return     CcSchedule The current object (for fluent API support)
 	 */
 	public function setDbCueOut($v)
 	{
-		// we treat '' as NULL for temporal objects because DateTime('') == DateTime('now')
-		// -- which is unexpected, to say the least.
-		if ($v === null || $v === '') {
-			$dt = null;
-		} elseif ($v instanceof DateTime) {
-			$dt = $v;
-		} else {
-			// some string/numeric value passed; we normalize that so that we can
-			// validate it.
-			try {
-				if (is_numeric($v)) { // if it's a unix timestamp
-					$dt = new DateTime('@'.$v, new DateTimeZone('UTC'));
-					// We have to explicitly specify and then change the time zone because of a
-					// DateTime bug: http://bugs.php.net/bug.php?id=43003
-					$dt->setTimeZone(new DateTimeZone(date_default_timezone_get()));
-				} else {
-					$dt = new DateTime($v);
-				}
-			} catch (Exception $x) {
-				throw new PropelException('Error parsing date/time value: ' . var_export($v, true), $x);
-			}
+		if ($v !== null) {
+			$v = (string) $v;
 		}
 
-		if ( $this->cue_out !== null || $dt !== null ) {
-			// (nested ifs are a little easier to read in this case)
-
-			$currNorm = ($this->cue_out !== null && $tmpDt = new DateTime($this->cue_out)) ? $tmpDt->format('H:i:s') : null;
-			$newNorm = ($dt !== null) ? $dt->format('H:i:s') : null;
-
-			if ( ($currNorm !== $newNorm) // normalized values don't match 
-					|| ($dt->format('H:i:s') === '00:00:00') // or the entered value matches the default
-					)
-			{
-				$this->cue_out = ($dt ? $dt->format('H:i:s') : null);
-				$this->modifiedColumns[] = CcSchedulePeer::CUE_OUT;
-			}
-		} // if either are not null
+		if ($this->cue_out !== $v || $this->isNew()) {
+			$this->cue_out = $v;
+			$this->modifiedColumns[] = CcSchedulePeer::CUE_OUT;
+		}
 
 		return $this;
 	} // setDbCueOut()
@@ -856,6 +715,26 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 		return $this;
 	} // setDbInstanceId()
 
+	/**
+	 * Set the value of [status] column.
+	 * 
+	 * @param      int $v new value
+	 * @return     CcSchedule The current object (for fluent API support)
+	 */
+	public function setDbStatus($v)
+	{
+		if ($v !== null) {
+			$v = (int) $v;
+		}
+
+		if ($this->status !== $v || $this->isNew()) {
+			$this->status = $v;
+			$this->modifiedColumns[] = CcSchedulePeer::STATUS;
+		}
+
+		return $this;
+	} // setDbStatus()
+
 	/**
 	 * Indicates whether the columns in this object are only set to default values.
 	 *
@@ -890,6 +769,10 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 				return false;
 			}
 
+			if ($this->status !== 1) {
+				return false;
+			}
+
 		// otherwise, everything was equal, so return TRUE
 		return true;
 	} // hasOnlyDefaultValues()
@@ -923,6 +806,7 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 			$this->cue_out = ($row[$startcol + 8] !== null) ? (string) $row[$startcol + 8] : null;
 			$this->media_item_played = ($row[$startcol + 9] !== null) ? (boolean) $row[$startcol + 9] : null;
 			$this->instance_id = ($row[$startcol + 10] !== null) ? (int) $row[$startcol + 10] : null;
+			$this->status = ($row[$startcol + 11] !== null) ? (int) $row[$startcol + 11] : null;
 			$this->resetModified();
 
 			$this->setNew(false);
@@ -931,7 +815,7 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 				$this->ensureConsistency();
 			}
 
-			return $startcol + 11; // 11 = CcSchedulePeer::NUM_COLUMNS - CcSchedulePeer::NUM_LAZY_LOAD_COLUMNS).
+			return $startcol + 12; // 12 = CcSchedulePeer::NUM_COLUMNS - CcSchedulePeer::NUM_LAZY_LOAD_COLUMNS).
 
 		} catch (Exception $e) {
 			throw new PropelException("Error populating CcSchedule object", $e);
@@ -1310,6 +1194,9 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 			case 10:
 				return $this->getDbInstanceId();
 				break;
+			case 11:
+				return $this->getDbStatus();
+				break;
 			default:
 				return null;
 				break;
@@ -1345,6 +1232,7 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 			$keys[8] => $this->getDbCueOut(),
 			$keys[9] => $this->getDbMediaItemPlayed(),
 			$keys[10] => $this->getDbInstanceId(),
+			$keys[11] => $this->getDbStatus(),
 		);
 		if ($includeForeignObjects) {
 			if (null !== $this->aCcShowInstances) {
@@ -1417,6 +1305,9 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 			case 10:
 				$this->setDbInstanceId($value);
 				break;
+			case 11:
+				$this->setDbStatus($value);
+				break;
 		} // switch()
 	}
 
@@ -1452,6 +1343,7 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 		if (array_key_exists($keys[8], $arr)) $this->setDbCueOut($arr[$keys[8]]);
 		if (array_key_exists($keys[9], $arr)) $this->setDbMediaItemPlayed($arr[$keys[9]]);
 		if (array_key_exists($keys[10], $arr)) $this->setDbInstanceId($arr[$keys[10]]);
+		if (array_key_exists($keys[11], $arr)) $this->setDbStatus($arr[$keys[11]]);
 	}
 
 	/**
@@ -1474,6 +1366,7 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 		if ($this->isColumnModified(CcSchedulePeer::CUE_OUT)) $criteria->add(CcSchedulePeer::CUE_OUT, $this->cue_out);
 		if ($this->isColumnModified(CcSchedulePeer::MEDIA_ITEM_PLAYED)) $criteria->add(CcSchedulePeer::MEDIA_ITEM_PLAYED, $this->media_item_played);
 		if ($this->isColumnModified(CcSchedulePeer::INSTANCE_ID)) $criteria->add(CcSchedulePeer::INSTANCE_ID, $this->instance_id);
+		if ($this->isColumnModified(CcSchedulePeer::STATUS)) $criteria->add(CcSchedulePeer::STATUS, $this->status);
 
 		return $criteria;
 	}
@@ -1545,6 +1438,7 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 		$copyObj->setDbCueOut($this->cue_out);
 		$copyObj->setDbMediaItemPlayed($this->media_item_played);
 		$copyObj->setDbInstanceId($this->instance_id);
+		$copyObj->setDbStatus($this->status);
 
 		$copyObj->setNew(true);
 		$copyObj->setDbId(NULL); // this is a auto-increment column, so set to default value
@@ -1706,6 +1600,7 @@ abstract class BaseCcSchedule extends BaseObject  implements Persistent
 		$this->cue_out = null;
 		$this->media_item_played = null;
 		$this->instance_id = null;
+		$this->status = null;
 		$this->alreadyInSave = false;
 		$this->alreadyInValidation = false;
 		$this->clearAllReferences();
diff --git a/airtime_mvc/application/models/airtime/om/BaseCcSchedulePeer.php b/airtime_mvc/application/models/airtime/om/BaseCcSchedulePeer.php
index e11d284a7..2aa1199f7 100644
--- a/airtime_mvc/application/models/airtime/om/BaseCcSchedulePeer.php
+++ b/airtime_mvc/application/models/airtime/om/BaseCcSchedulePeer.php
@@ -26,7 +26,7 @@ abstract class BaseCcSchedulePeer {
 	const TM_CLASS = 'CcScheduleTableMap';
 	
 	/** The total number of columns. */
-	const NUM_COLUMNS = 11;
+	const NUM_COLUMNS = 12;
 
 	/** The number of lazy-loaded columns. */
 	const NUM_LAZY_LOAD_COLUMNS = 0;
@@ -64,6 +64,9 @@ abstract class BaseCcSchedulePeer {
 	/** the column name for the INSTANCE_ID field */
 	const INSTANCE_ID = 'cc_schedule.INSTANCE_ID';
 
+	/** the column name for the STATUS field */
+	const STATUS = 'cc_schedule.STATUS';
+
 	/**
 	 * An identiy map to hold any loaded instances of CcSchedule objects.
 	 * This must be public so that other peer classes can access this when hydrating from JOIN
@@ -80,12 +83,12 @@ abstract class BaseCcSchedulePeer {
 	 * e.g. self::$fieldNames[self::TYPE_PHPNAME][0] = 'Id'
 	 */
 	private static $fieldNames = array (
-		BasePeer::TYPE_PHPNAME => array ('DbId', 'DbStarts', 'DbEnds', 'DbFileId', 'DbClipLength', 'DbFadeIn', 'DbFadeOut', 'DbCueIn', 'DbCueOut', 'DbMediaItemPlayed', 'DbInstanceId', ),
-		BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbStarts', 'dbEnds', 'dbFileId', 'dbClipLength', 'dbFadeIn', 'dbFadeOut', 'dbCueIn', 'dbCueOut', 'dbMediaItemPlayed', 'dbInstanceId', ),
-		BasePeer::TYPE_COLNAME => array (self::ID, self::STARTS, self::ENDS, self::FILE_ID, self::CLIP_LENGTH, self::FADE_IN, self::FADE_OUT, self::CUE_IN, self::CUE_OUT, self::MEDIA_ITEM_PLAYED, self::INSTANCE_ID, ),
-		BasePeer::TYPE_RAW_COLNAME => array ('ID', 'STARTS', 'ENDS', 'FILE_ID', 'CLIP_LENGTH', 'FADE_IN', 'FADE_OUT', 'CUE_IN', 'CUE_OUT', 'MEDIA_ITEM_PLAYED', 'INSTANCE_ID', ),
-		BasePeer::TYPE_FIELDNAME => array ('id', 'starts', 'ends', 'file_id', 'clip_length', 'fade_in', 'fade_out', 'cue_in', 'cue_out', 'media_item_played', 'instance_id', ),
-		BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, )
+		BasePeer::TYPE_PHPNAME => array ('DbId', 'DbStarts', 'DbEnds', 'DbFileId', 'DbClipLength', 'DbFadeIn', 'DbFadeOut', 'DbCueIn', 'DbCueOut', 'DbMediaItemPlayed', 'DbInstanceId', 'DbStatus', ),
+		BasePeer::TYPE_STUDLYPHPNAME => array ('dbId', 'dbStarts', 'dbEnds', 'dbFileId', 'dbClipLength', 'dbFadeIn', 'dbFadeOut', 'dbCueIn', 'dbCueOut', 'dbMediaItemPlayed', 'dbInstanceId', 'dbStatus', ),
+		BasePeer::TYPE_COLNAME => array (self::ID, self::STARTS, self::ENDS, self::FILE_ID, self::CLIP_LENGTH, self::FADE_IN, self::FADE_OUT, self::CUE_IN, self::CUE_OUT, self::MEDIA_ITEM_PLAYED, self::INSTANCE_ID, self::STATUS, ),
+		BasePeer::TYPE_RAW_COLNAME => array ('ID', 'STARTS', 'ENDS', 'FILE_ID', 'CLIP_LENGTH', 'FADE_IN', 'FADE_OUT', 'CUE_IN', 'CUE_OUT', 'MEDIA_ITEM_PLAYED', 'INSTANCE_ID', 'STATUS', ),
+		BasePeer::TYPE_FIELDNAME => array ('id', 'starts', 'ends', 'file_id', 'clip_length', 'fade_in', 'fade_out', 'cue_in', 'cue_out', 'media_item_played', 'instance_id', 'status', ),
+		BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, )
 	);
 
 	/**
@@ -95,12 +98,12 @@ abstract class BaseCcSchedulePeer {
 	 * e.g. self::$fieldNames[BasePeer::TYPE_PHPNAME]['Id'] = 0
 	 */
 	private static $fieldKeys = array (
-		BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbStarts' => 1, 'DbEnds' => 2, 'DbFileId' => 3, 'DbClipLength' => 4, 'DbFadeIn' => 5, 'DbFadeOut' => 6, 'DbCueIn' => 7, 'DbCueOut' => 8, 'DbMediaItemPlayed' => 9, 'DbInstanceId' => 10, ),
-		BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbStarts' => 1, 'dbEnds' => 2, 'dbFileId' => 3, 'dbClipLength' => 4, 'dbFadeIn' => 5, 'dbFadeOut' => 6, 'dbCueIn' => 7, 'dbCueOut' => 8, 'dbMediaItemPlayed' => 9, 'dbInstanceId' => 10, ),
-		BasePeer::TYPE_COLNAME => array (self::ID => 0, self::STARTS => 1, self::ENDS => 2, self::FILE_ID => 3, self::CLIP_LENGTH => 4, self::FADE_IN => 5, self::FADE_OUT => 6, self::CUE_IN => 7, self::CUE_OUT => 8, self::MEDIA_ITEM_PLAYED => 9, self::INSTANCE_ID => 10, ),
-		BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'STARTS' => 1, 'ENDS' => 2, 'FILE_ID' => 3, 'CLIP_LENGTH' => 4, 'FADE_IN' => 5, 'FADE_OUT' => 6, 'CUE_IN' => 7, 'CUE_OUT' => 8, 'MEDIA_ITEM_PLAYED' => 9, 'INSTANCE_ID' => 10, ),
-		BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'starts' => 1, 'ends' => 2, 'file_id' => 3, 'clip_length' => 4, 'fade_in' => 5, 'fade_out' => 6, 'cue_in' => 7, 'cue_out' => 8, 'media_item_played' => 9, 'instance_id' => 10, ),
-		BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, )
+		BasePeer::TYPE_PHPNAME => array ('DbId' => 0, 'DbStarts' => 1, 'DbEnds' => 2, 'DbFileId' => 3, 'DbClipLength' => 4, 'DbFadeIn' => 5, 'DbFadeOut' => 6, 'DbCueIn' => 7, 'DbCueOut' => 8, 'DbMediaItemPlayed' => 9, 'DbInstanceId' => 10, 'DbStatus' => 11, ),
+		BasePeer::TYPE_STUDLYPHPNAME => array ('dbId' => 0, 'dbStarts' => 1, 'dbEnds' => 2, 'dbFileId' => 3, 'dbClipLength' => 4, 'dbFadeIn' => 5, 'dbFadeOut' => 6, 'dbCueIn' => 7, 'dbCueOut' => 8, 'dbMediaItemPlayed' => 9, 'dbInstanceId' => 10, 'dbStatus' => 11, ),
+		BasePeer::TYPE_COLNAME => array (self::ID => 0, self::STARTS => 1, self::ENDS => 2, self::FILE_ID => 3, self::CLIP_LENGTH => 4, self::FADE_IN => 5, self::FADE_OUT => 6, self::CUE_IN => 7, self::CUE_OUT => 8, self::MEDIA_ITEM_PLAYED => 9, self::INSTANCE_ID => 10, self::STATUS => 11, ),
+		BasePeer::TYPE_RAW_COLNAME => array ('ID' => 0, 'STARTS' => 1, 'ENDS' => 2, 'FILE_ID' => 3, 'CLIP_LENGTH' => 4, 'FADE_IN' => 5, 'FADE_OUT' => 6, 'CUE_IN' => 7, 'CUE_OUT' => 8, 'MEDIA_ITEM_PLAYED' => 9, 'INSTANCE_ID' => 10, 'STATUS' => 11, ),
+		BasePeer::TYPE_FIELDNAME => array ('id' => 0, 'starts' => 1, 'ends' => 2, 'file_id' => 3, 'clip_length' => 4, 'fade_in' => 5, 'fade_out' => 6, 'cue_in' => 7, 'cue_out' => 8, 'media_item_played' => 9, 'instance_id' => 10, 'status' => 11, ),
+		BasePeer::TYPE_NUM => array (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, )
 	);
 
 	/**
@@ -183,6 +186,7 @@ abstract class BaseCcSchedulePeer {
 			$criteria->addSelectColumn(CcSchedulePeer::CUE_OUT);
 			$criteria->addSelectColumn(CcSchedulePeer::MEDIA_ITEM_PLAYED);
 			$criteria->addSelectColumn(CcSchedulePeer::INSTANCE_ID);
+			$criteria->addSelectColumn(CcSchedulePeer::STATUS);
 		} else {
 			$criteria->addSelectColumn($alias . '.ID');
 			$criteria->addSelectColumn($alias . '.STARTS');
@@ -195,6 +199,7 @@ abstract class BaseCcSchedulePeer {
 			$criteria->addSelectColumn($alias . '.CUE_OUT');
 			$criteria->addSelectColumn($alias . '.MEDIA_ITEM_PLAYED');
 			$criteria->addSelectColumn($alias . '.INSTANCE_ID');
+			$criteria->addSelectColumn($alias . '.STATUS');
 		}
 	}
 
diff --git a/airtime_mvc/application/models/airtime/om/BaseCcScheduleQuery.php b/airtime_mvc/application/models/airtime/om/BaseCcScheduleQuery.php
index 4c661ad56..dc26a7311 100644
--- a/airtime_mvc/application/models/airtime/om/BaseCcScheduleQuery.php
+++ b/airtime_mvc/application/models/airtime/om/BaseCcScheduleQuery.php
@@ -17,6 +17,7 @@
  * @method     CcScheduleQuery orderByDbCueOut($order = Criteria::ASC) Order by the cue_out column
  * @method     CcScheduleQuery orderByDbMediaItemPlayed($order = Criteria::ASC) Order by the media_item_played column
  * @method     CcScheduleQuery orderByDbInstanceId($order = Criteria::ASC) Order by the instance_id column
+ * @method     CcScheduleQuery orderByDbStatus($order = Criteria::ASC) Order by the status column
  *
  * @method     CcScheduleQuery groupByDbId() Group by the id column
  * @method     CcScheduleQuery groupByDbStarts() Group by the starts column
@@ -29,6 +30,7 @@
  * @method     CcScheduleQuery groupByDbCueOut() Group by the cue_out column
  * @method     CcScheduleQuery groupByDbMediaItemPlayed() Group by the media_item_played column
  * @method     CcScheduleQuery groupByDbInstanceId() Group by the instance_id column
+ * @method     CcScheduleQuery groupByDbStatus() Group by the status column
  *
  * @method     CcScheduleQuery leftJoin($relation) Adds a LEFT JOIN clause to the query
  * @method     CcScheduleQuery rightJoin($relation) Adds a RIGHT JOIN clause to the query
@@ -56,6 +58,7 @@
  * @method     CcSchedule findOneByDbCueOut(string $cue_out) Return the first CcSchedule filtered by the cue_out column
  * @method     CcSchedule findOneByDbMediaItemPlayed(boolean $media_item_played) Return the first CcSchedule filtered by the media_item_played column
  * @method     CcSchedule findOneByDbInstanceId(int $instance_id) Return the first CcSchedule filtered by the instance_id column
+ * @method     CcSchedule findOneByDbStatus(int $status) Return the first CcSchedule filtered by the status column
  *
  * @method     array findByDbId(int $id) Return CcSchedule objects filtered by the id column
  * @method     array findByDbStarts(string $starts) Return CcSchedule objects filtered by the starts column
@@ -68,6 +71,7 @@
  * @method     array findByDbCueOut(string $cue_out) Return CcSchedule objects filtered by the cue_out column
  * @method     array findByDbMediaItemPlayed(boolean $media_item_played) Return CcSchedule objects filtered by the media_item_played column
  * @method     array findByDbInstanceId(int $instance_id) Return CcSchedule objects filtered by the instance_id column
+ * @method     array findByDbStatus(int $status) Return CcSchedule objects filtered by the status column
  *
  * @package    propel.generator.airtime.om
  */
@@ -290,29 +294,20 @@ abstract class BaseCcScheduleQuery extends ModelCriteria
 	/**
 	 * Filter the query on the clip_length column
 	 * 
-	 * @param     string|array $dbClipLength The value to use as filter.
-	 *            Accepts an associative array('min' => $minValue, 'max' => $maxValue)
+	 * @param     string $dbClipLength The value to use as filter.
+	 *            Accepts wildcards (* and % trigger a LIKE)
 	 * @param     string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
 	 *
 	 * @return    CcScheduleQuery The current query, for fluid interface
 	 */
 	public function filterByDbClipLength($dbClipLength = null, $comparison = null)
 	{
-		if (is_array($dbClipLength)) {
-			$useMinMax = false;
-			if (isset($dbClipLength['min'])) {
-				$this->addUsingAlias(CcSchedulePeer::CLIP_LENGTH, $dbClipLength['min'], Criteria::GREATER_EQUAL);
-				$useMinMax = true;
-			}
-			if (isset($dbClipLength['max'])) {
-				$this->addUsingAlias(CcSchedulePeer::CLIP_LENGTH, $dbClipLength['max'], Criteria::LESS_EQUAL);
-				$useMinMax = true;
-			}
-			if ($useMinMax) {
-				return $this;
-			}
-			if (null === $comparison) {
+		if (null === $comparison) {
+			if (is_array($dbClipLength)) {
 				$comparison = Criteria::IN;
+			} elseif (preg_match('/[\%\*]/', $dbClipLength)) {
+				$dbClipLength = str_replace('*', '%', $dbClipLength);
+				$comparison = Criteria::LIKE;
 			}
 		}
 		return $this->addUsingAlias(CcSchedulePeer::CLIP_LENGTH, $dbClipLength, $comparison);
@@ -383,29 +378,20 @@ abstract class BaseCcScheduleQuery extends ModelCriteria
 	/**
 	 * Filter the query on the cue_in column
 	 * 
-	 * @param     string|array $dbCueIn The value to use as filter.
-	 *            Accepts an associative array('min' => $minValue, 'max' => $maxValue)
+	 * @param     string $dbCueIn The value to use as filter.
+	 *            Accepts wildcards (* and % trigger a LIKE)
 	 * @param     string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
 	 *
 	 * @return    CcScheduleQuery The current query, for fluid interface
 	 */
 	public function filterByDbCueIn($dbCueIn = null, $comparison = null)
 	{
-		if (is_array($dbCueIn)) {
-			$useMinMax = false;
-			if (isset($dbCueIn['min'])) {
-				$this->addUsingAlias(CcSchedulePeer::CUE_IN, $dbCueIn['min'], Criteria::GREATER_EQUAL);
-				$useMinMax = true;
-			}
-			if (isset($dbCueIn['max'])) {
-				$this->addUsingAlias(CcSchedulePeer::CUE_IN, $dbCueIn['max'], Criteria::LESS_EQUAL);
-				$useMinMax = true;
-			}
-			if ($useMinMax) {
-				return $this;
-			}
-			if (null === $comparison) {
+		if (null === $comparison) {
+			if (is_array($dbCueIn)) {
 				$comparison = Criteria::IN;
+			} elseif (preg_match('/[\%\*]/', $dbCueIn)) {
+				$dbCueIn = str_replace('*', '%', $dbCueIn);
+				$comparison = Criteria::LIKE;
 			}
 		}
 		return $this->addUsingAlias(CcSchedulePeer::CUE_IN, $dbCueIn, $comparison);
@@ -414,29 +400,20 @@ abstract class BaseCcScheduleQuery extends ModelCriteria
 	/**
 	 * Filter the query on the cue_out column
 	 * 
-	 * @param     string|array $dbCueOut The value to use as filter.
-	 *            Accepts an associative array('min' => $minValue, 'max' => $maxValue)
+	 * @param     string $dbCueOut The value to use as filter.
+	 *            Accepts wildcards (* and % trigger a LIKE)
 	 * @param     string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
 	 *
 	 * @return    CcScheduleQuery The current query, for fluid interface
 	 */
 	public function filterByDbCueOut($dbCueOut = null, $comparison = null)
 	{
-		if (is_array($dbCueOut)) {
-			$useMinMax = false;
-			if (isset($dbCueOut['min'])) {
-				$this->addUsingAlias(CcSchedulePeer::CUE_OUT, $dbCueOut['min'], Criteria::GREATER_EQUAL);
-				$useMinMax = true;
-			}
-			if (isset($dbCueOut['max'])) {
-				$this->addUsingAlias(CcSchedulePeer::CUE_OUT, $dbCueOut['max'], Criteria::LESS_EQUAL);
-				$useMinMax = true;
-			}
-			if ($useMinMax) {
-				return $this;
-			}
-			if (null === $comparison) {
+		if (null === $comparison) {
+			if (is_array($dbCueOut)) {
 				$comparison = Criteria::IN;
+			} elseif (preg_match('/[\%\*]/', $dbCueOut)) {
+				$dbCueOut = str_replace('*', '%', $dbCueOut);
+				$comparison = Criteria::LIKE;
 			}
 		}
 		return $this->addUsingAlias(CcSchedulePeer::CUE_OUT, $dbCueOut, $comparison);
@@ -490,6 +467,37 @@ abstract class BaseCcScheduleQuery extends ModelCriteria
 		return $this->addUsingAlias(CcSchedulePeer::INSTANCE_ID, $dbInstanceId, $comparison);
 	}
 
+	/**
+	 * Filter the query on the status column
+	 * 
+	 * @param     int|array $dbStatus The value to use as filter.
+	 *            Accepts an associative array('min' => $minValue, 'max' => $maxValue)
+	 * @param     string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL
+	 *
+	 * @return    CcScheduleQuery The current query, for fluid interface
+	 */
+	public function filterByDbStatus($dbStatus = null, $comparison = null)
+	{
+		if (is_array($dbStatus)) {
+			$useMinMax = false;
+			if (isset($dbStatus['min'])) {
+				$this->addUsingAlias(CcSchedulePeer::STATUS, $dbStatus['min'], Criteria::GREATER_EQUAL);
+				$useMinMax = true;
+			}
+			if (isset($dbStatus['max'])) {
+				$this->addUsingAlias(CcSchedulePeer::STATUS, $dbStatus['max'], Criteria::LESS_EQUAL);
+				$useMinMax = true;
+			}
+			if ($useMinMax) {
+				return $this;
+			}
+			if (null === $comparison) {
+				$comparison = Criteria::IN;
+			}
+		}
+		return $this->addUsingAlias(CcSchedulePeer::STATUS, $dbStatus, $comparison);
+	}
+
 	/**
 	 * Filter the query by a related CcShowInstances object
 	 *
diff --git a/airtime_mvc/application/models/formatters/TimeFilledFormatter.php b/airtime_mvc/application/models/formatters/TimeFilledFormatter.php
new file mode 100644
index 000000000..6a894d194
--- /dev/null
+++ b/airtime_mvc/application/models/formatters/TimeFilledFormatter.php
@@ -0,0 +1,46 @@
+<?php
+
+class TimeFilledFormatter {
+
+    /**
+     * @string seconds
+     */
+    private $_seconds;
+
+    /*
+     * @param string $seconds
+     */
+    public function __construct($seconds)
+    {
+        $this->_seconds = $seconds;
+    }
+
+    public function format()
+    {
+        $formatted = "";
+        $sign = ($this->_seconds < 0) ? "-" : "+";
+
+        $time = Application_Model_Playlist::secondsToPlaylistTime(abs($this->_seconds));
+        Logging::log("time is: ".$time);
+        $info = explode(":", $time);
+
+        $formatted .= $sign;
+
+        if (intval($info[0]) > 0) {
+            $info[0] = ltrim($info[0], "0");
+            $formatted .= " {$info[0]}h";
+        }
+
+        if (intval($info[1]) > 0) {
+            $info[1] = ltrim($info[1], "0");
+            $formatted .= " {$info[1]}m";
+        }
+
+        if (intval($info[2]) > 0) {
+            $sec = round($info[2], 0);
+            $formatted .= " {$sec}s";
+        }
+
+        return $formatted;
+    }
+}
\ No newline at end of file
diff --git a/airtime_mvc/application/views/scripts/library/contents.phtml b/airtime_mvc/application/views/scripts/library/contents.phtml
deleted file mode 100644
index e69de29bb..000000000
diff --git a/airtime_mvc/application/views/scripts/library/context-menu.phtml b/airtime_mvc/application/views/scripts/library/context-menu.phtml
deleted file mode 100644
index e69de29bb..000000000
diff --git a/airtime_mvc/application/views/scripts/library/delete.phtml b/airtime_mvc/application/views/scripts/library/delete.phtml
deleted file mode 100644
index 5cbb9a545..000000000
--- a/airtime_mvc/application/views/scripts/library/delete.phtml
+++ /dev/null
@@ -1 +0,0 @@
-<br /><br /><center>View script for controller <b>Library</b> and script/action name <b>delete</b></center>
\ No newline at end of file
diff --git a/airtime_mvc/application/views/scripts/library/libraryTablePartial.phtml b/airtime_mvc/application/views/scripts/library/libraryTablePartial.phtml
deleted file mode 100644
index 52074462d..000000000
--- a/airtime_mvc/application/views/scripts/library/libraryTablePartial.phtml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?php // libraryTablePartial.phtml ?>
-<tr id="<?php echo substr($this->ftype, 0, 2) ?>_<?php echo $this->id ?>">
-    <td><?php echo $this->track_title ?></td>
-    <td><?php echo $this->artist_name ?></td>
-	<td><?php echo $this->album_title ?></td>
-    <td><?php echo $this->track_number ?></td>
-	<td><?php echo $this->length ?></td>
-</tr>
diff --git a/airtime_mvc/application/views/scripts/library/search.phtml b/airtime_mvc/application/views/scripts/library/search.phtml
deleted file mode 100644
index 5fb9621a7..000000000
--- a/airtime_mvc/application/views/scripts/library/search.phtml
+++ /dev/null
@@ -1 +0,0 @@
-<br /><br /><center>View script for controller <b>Library</b> and script/action name <b>search</b></center>
\ No newline at end of file
diff --git a/airtime_mvc/application/views/scripts/library/update.phtml b/airtime_mvc/application/views/scripts/library/update.phtml
deleted file mode 100644
index 52eb9608b..000000000
--- a/airtime_mvc/application/views/scripts/library/update.phtml
+++ /dev/null
@@ -1,3 +0,0 @@
-<?php
-
-	echo $this->partialLoop('library/libraryTablePartial.phtml', $this->files);
diff --git a/airtime_mvc/application/views/scripts/playlist/update.phtml b/airtime_mvc/application/views/scripts/playlist/update.phtml
index bd55a9f94..24ddd0882 100644
--- a/airtime_mvc/application/views/scripts/playlist/update.phtml
+++ b/airtime_mvc/application/views/scripts/playlist/update.phtml
@@ -4,11 +4,11 @@ if (count($items)) : ?>
 
 <?php $i = 0; ?>
 <?php foreach($items as $item) : ?>
-    <li class="ui-state-default" id="spl_<?php echo $item["id"] ?>" unqid="<?php echo $item["CcFiles"]["gunid"]."_".$i; ?>">
+    <li class="ui-state-default" id="spl_<?php echo $item["id"] ?>" unqid="<?php echo $item["CcFiles"]["gunid"]."_".$item["id"]; ?>">
         <div class="list-item-container">
             <a href="javascript:void(0);" class="big_play"
             onclick="audioPreview('<?php echo $item["CcFiles"]["gunid"].".".pathinfo($item["CcFiles"]["filepath"], PATHINFO_EXTENSION);?>',
-            'spl_<?php echo $i ?>')"><span class="ui-icon ui-icon-play"></span></a>
+            'spl_<?php echo $item["id"] ?>')"><span class="ui-icon ui-icon-play"></span></a>
 
             <div class="text-row top">
                 <span class="spl_playlength"><?php echo $item["cliplength"] ?></span>
diff --git a/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml b/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml
new file mode 100644
index 000000000..8c8bee2b5
--- /dev/null
+++ b/airtime_mvc/application/views/scripts/showbuilder/builderDialog.phtml
@@ -0,0 +1,9 @@
+<div class="wrapper">
+    <div id="library_content" class="tabs ui-widget ui-widget-content block-shadow alpha-block padded">
+        <div id="import_status" style="display:none">File import in progress...</div>
+        <table id="library_display" cellpadding="0" cellspacing="0" class="datatable"></table>
+    </div>
+    <div id="show_builder" class="ui-widget ui-widget-content block-shadow omega-block padded">
+        <table id="show_builder_table" cellpadding="0" cellspacing="0" class="datatable"></table>
+    </div>
+</div>
\ No newline at end of file
diff --git a/airtime_mvc/build/schema.xml b/airtime_mvc/build/schema.xml
index 85aca369f..3060cbd19 100644
--- a/airtime_mvc/build/schema.xml
+++ b/airtime_mvc/build/schema.xml
@@ -237,9 +237,9 @@
     <column name="playlist_id" phpName="DbPlaylistId" type="INTEGER" required="false"/>
     <column name="file_id" phpName="DbFileId" type="INTEGER" required="false"/>
     <column name="position" phpName="DbPosition" type="INTEGER" required="false"/>
-    <column name="cliplength" phpName="DbCliplength" type="TIME" required="false" defaultValue="00:00:00"/>
-    <column name="cuein" phpName="DbCuein" type="TIME" required="false" defaultValue="00:00:00"/>
-    <column name="cueout" phpName="DbCueout" type="TIME" required="false" defaultValue="00:00:00"/>
+    <column name="cliplength" phpName="DbCliplength" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
+    <column name="cuein" phpName="DbCuein" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
+    <column name="cueout" phpName="DbCueout" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
     <column name="fadein" phpName="DbFadein" type="TIME" required="false" defaultValue="00:00:00"/>
     <column name="fadeout" phpName="DbFadeout" type="TIME" required="false" defaultValue="00:00:00"/>
     <foreign-key foreignTable="cc_files" name="cc_playlistcontents_file_id_fkey" onDelete="CASCADE">
@@ -273,13 +273,14 @@
     <column name="starts" phpName="DbStarts" type="TIMESTAMP" required="true"/>
     <column name="ends" phpName="DbEnds" type="TIMESTAMP" required="true"/>
     <column name="file_id" phpName="DbFileId" type="INTEGER" required="false"/>
-    <column name="clip_length" phpName="DbClipLength" type="TIME" required="false" defaultValue="00:00:00"/>
+    <column name="clip_length" phpName="DbClipLength" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
     <column name="fade_in" phpName="DbFadeIn" type="TIME" required="false" defaultValue="00:00:00"/>
     <column name="fade_out" phpName="DbFadeOut" type="TIME" required="false" defaultValue="00:00:00"/>
-    <column name="cue_in" phpName="DbCueIn" type="TIME" required="false" defaultValue="00:00:00"/>
-    <column name="cue_out" phpName="DbCueOut" type="TIME" required="false" defaultValue="00:00:00"/>
+    <column name="cue_in" phpName="DbCueIn" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
+    <column name="cue_out" phpName="DbCueOut" type="VARCHAR" sqlType="interval" required="false" defaultValue="00:00:00"/>
     <column name="media_item_played" phpName="DbMediaItemPlayed" type="BOOLEAN" required="false" defaultValue="0"/>
     <column name="instance_id" phpName="DbInstanceId" type="INTEGER" required="true"/>
+    <column name="status" phpName="DbStatus" type="SMALLINT" required="true" defaultValue="1"/>
     <!-- This foreign key is still useful even though it may seem we don't ever delete cc_show_instances anymore.
         We will do delete them in some cases (when editing a show and changing the repeating days of the week
         for example. \
diff --git a/airtime_mvc/build/sql/schema.sql b/airtime_mvc/build/sql/schema.sql
index 455ccbf5c..cc85b26f0 100644
--- a/airtime_mvc/build/sql/schema.sql
+++ b/airtime_mvc/build/sql/schema.sql
@@ -315,9 +315,9 @@ CREATE TABLE "cc_playlistcontents"
 	"playlist_id" INTEGER,
 	"file_id" INTEGER,
 	"position" INTEGER,
-	"cliplength" TIME default '00:00:00',
-	"cuein" TIME default '00:00:00',
-	"cueout" TIME default '00:00:00',
+	"cliplength" interval default '00:00:00',
+	"cuein" interval default '00:00:00',
+	"cueout" interval default '00:00:00',
 	"fadein" TIME default '00:00:00',
 	"fadeout" TIME default '00:00:00',
 	PRIMARY KEY ("id")
@@ -364,13 +364,14 @@ CREATE TABLE "cc_schedule"
 	"starts" TIMESTAMP  NOT NULL,
 	"ends" TIMESTAMP  NOT NULL,
 	"file_id" INTEGER,
-	"clip_length" TIME default '00:00:00',
+	"clip_length" interval default '00:00:00',
 	"fade_in" TIME default '00:00:00',
 	"fade_out" TIME default '00:00:00',
-	"cue_in" TIME default '00:00:00',
-	"cue_out" TIME default '00:00:00',
+	"cue_in" interval default '00:00:00',
+	"cue_out" interval default '00:00:00',
 	"media_item_played" BOOLEAN default 'f',
 	"instance_id" INTEGER  NOT NULL,
+	"status" INT2 default 1 NOT NULL,
 	PRIMARY KEY ("id")
 );
 
diff --git a/airtime_mvc/public/css/datatables/css/ColVis.css b/airtime_mvc/public/css/datatables/css/ColVis.css
index 95987b876..f4be403c1 100644
--- a/airtime_mvc/public/css/datatables/css/ColVis.css
+++ b/airtime_mvc/public/css/datatables/css/ColVis.css
@@ -30,7 +30,7 @@ button.ColVis_Button::-moz-focus-inner {
 
 div.ColVis_collectionBackground {
 	background-color: black;
-	z-index: 996;
+	z-index: 1003;
 }
 
 div.ColVis_collection {
@@ -39,7 +39,7 @@ div.ColVis_collection {
 	background-color: #999;
 	padding: 3px;
 	border: 1px solid #ccc;
-	z-index: 998;
+	z-index: 1005;
 }
 
 div.ColVis_collection button.ColVis_Button {
@@ -51,7 +51,7 @@ div.ColVis_collection button.ColVis_Button {
 
 div.ColVis_catcher {
 	position: absolute;
-	z-index: 997;
+	z-index: 1004;
 }
 
 .disabled {
diff --git a/airtime_mvc/public/css/media_library.css b/airtime_mvc/public/css/media_library.css
index 20b60b4bc..97de2e91c 100644
--- a/airtime_mvc/public/css/media_library.css
+++ b/airtime_mvc/public/css/media_library.css
@@ -79,3 +79,8 @@
 .library_year {
     text-align: center;
 }
+
+.library_sr,
+.library_bitrate {
+	text-align: right;
+}
diff --git a/airtime_mvc/public/css/playlist_builder.css b/airtime_mvc/public/css/playlist_builder.css
index 30947f291..cef0a7eba 100644
--- a/airtime_mvc/public/css/playlist_builder.css
+++ b/airtime_mvc/public/css/playlist_builder.css
@@ -457,7 +457,5 @@ div.helper li {
 }
 
 li.spl_empty {
-    text-align: center;
     height: 56px;
-    border:2px dashed black;
 }
\ No newline at end of file
diff --git a/airtime_mvc/public/css/showbuilder.css b/airtime_mvc/public/css/showbuilder.css
index 9854c67b4..1afa1e7b4 100644
--- a/airtime_mvc/public/css/showbuilder.css
+++ b/airtime_mvc/public/css/showbuilder.css
@@ -34,4 +34,17 @@ tr.cursor-selected-row .marker {
 
 .sb-over {
     background-color:#ff3030;
+}
+
+.sb-now-playing {
+    background-color:#17eb25 !important;
+}
+
+.ui-dialog .wrapper {
+    margin: 0;
+    padding: 10px 0 0 0;
+}
+
+.ui-dialog .ui-buttonset {
+    margin-right: 0 !important;
 }
\ No newline at end of file
diff --git a/airtime_mvc/public/css/styles.css b/airtime_mvc/public/css/styles.css
index 9845cd260..5215c675e 100644
--- a/airtime_mvc/public/css/styles.css
+++ b/airtime_mvc/public/css/styles.css
@@ -601,6 +601,7 @@ dl.inline-list dd {
 }
 
 .dataTables_info {
+	float: left;
 	padding: 8px 0 0 8px;
 	font-size:12px;
 	color:#555555;
@@ -608,6 +609,7 @@ dl.inline-list dd {
 }
 
 .dataTables_paginate {
+	float: right;
 	padding: 8px 0 8px 8px;
 }
 .dataTables_paginate .ui-button {
@@ -618,7 +620,7 @@ dl.inline-list dd {
 }
 .dataTables_filter input {
     background: url("images/search_auto_bg.png") no-repeat scroll 0 0 #DDDDDD;
-    width: 60%;
+    width: 55%;
     border: 1px solid #5B5B5B;
     margin-left: -8px;
     padding: 4px 3px 4px 25px;
diff --git a/airtime_mvc/public/js/airtime/buttons/buttons.js b/airtime_mvc/public/js/airtime/buttons/buttons.js
new file mode 100644
index 000000000..e11814aae
--- /dev/null
+++ b/airtime_mvc/public/js/airtime/buttons/buttons.js
@@ -0,0 +1,29 @@
+var AIRTIME = (function(AIRTIME){
+	var mod,
+		DEFAULT_CLASS = 'ui-button ui-state-default',
+		DISABLED_CLASS = 'ui-state-disabled';
+	
+	if (AIRTIME.button === undefined) {
+		AIRTIME.button = {};
+	}
+	mod = AIRTIME.button;
+	
+	mod.enableButton = function(c) {
+		var button = $("."+c).find("button");
+		
+	    if (button.hasClass(DISABLED_CLASS)) {
+	        button.removeClass(DISABLED_CLASS);
+	    }
+	};
+
+	mod.disableButton = function(c) {
+		var button = $("."+c).find("button");
+		
+	    if (!button.hasClass(DISABLED_CLASS)) {
+	        button.addClass(DISABLED_CLASS);
+	    }
+	};
+	
+	return AIRTIME;
+	
+}(AIRTIME || {}));
\ No newline at end of file
diff --git a/airtime_mvc/public/js/airtime/library/events/library_playlistbuilder.js b/airtime_mvc/public/js/airtime/library/events/library_playlistbuilder.js
index 676bac86c..0178d5688 100644
--- a/airtime_mvc/public/js/airtime/library/events/library_playlistbuilder.js
+++ b/airtime_mvc/public/js/airtime/library/events/library_playlistbuilder.js
@@ -7,6 +7,24 @@ var AIRTIME = (function(AIRTIME){
 	
 	AIRTIME.library.events = {};
 	mod = AIRTIME.library.events;
+
+    mod.enableAddButtonCheck = function() {
+    	var selected = $('#library_display tr[id ^= "au"] input[type=checkbox]').filter(":checked"),
+    		sortable = $('#spl_sortable'),
+    		check = false;
+    	
+    	//make sure audioclips are selected and a playlist is currently open.
+    	if (selected.length !== 0 && sortable.length !== 0) {
+    		check = true;
+    	}
+    	
+    	if (check === true) {
+	    	AIRTIME.button.enableButton("library_group_add");
+	    }
+	    else {
+	    	AIRTIME.button.disableButton("library_group_add");
+	    }
+    };
 	
 	mod.fnRowCallback = function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
 		var $nRow = $(nRow);
@@ -63,22 +81,22 @@ var AIRTIME = (function(AIRTIME){
 	 */
 	mod.setupLibraryToolbar = function( oLibTable ) {
 		var aButtons,
-			fnResetCol,
 			fnAddSelectedItems;
 		
 		fnAddSelectedItems = function() {
 			var oLibTT = TableTools.fnGetInstance('library_display'),
 				aData = oLibTT.fnGetSelectedData(),
-				item,
+				i,
 				temp,
+				length,
 				aMediaIds = [];
 			
 			//process selected files/playlists.
-			for (item in aData) {
-				temp = aData[item];
-				if (temp !== null && temp.hasOwnProperty('id') && temp.ftype === "audioclip") {
+			for (i = 0, length = aData.length; i < length; i++) {
+				temp = aData[i];
+				if (temp.ftype === "audioclip") {
 					aMediaIds.push(temp.id);
-				} 	
+				}
 			}
 		
 			AIRTIME.playlist.fnAddItems(aMediaIds, undefined, 'after');
@@ -88,8 +106,8 @@ var AIRTIME = (function(AIRTIME){
 		//[1] = id 
 		//[2] = enabled
 		//[3] = click event
-		aButtons = [["Delete", "library_group_delete", true, AIRTIME.library.fnDeleteSelectedItems], 
-	                ["Add", "library_group_add", true, fnAddSelectedItems]];
+		aButtons = [["Delete", "library_group_delete", false, AIRTIME.library.fnDeleteSelectedItems], 
+	                ["Add", "library_group_add", false, fnAddSelectedItems]];
 		
 		addToolBarButtonsLibrary(aButtons);
 	};
diff --git a/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js b/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js
index 822b2f82f..c18d36b9e 100644
--- a/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js
+++ b/airtime_mvc/public/js/airtime/library/events/library_showbuilder.js
@@ -8,6 +8,24 @@ var AIRTIME = (function(AIRTIME){
 	AIRTIME.library.events = {};
 	mod = AIRTIME.library.events;
 	
+	mod.enableAddButtonCheck = function() {
+    	var selected = $('#library_display tr input[type=checkbox]').filter(":checked"),
+    		cursor = $('tr.cursor-selected-row'),
+    		check = false;
+    	
+    	//make sure library items are selected and a cursor is selected.
+    	if (selected.length !== 0 && cursor.length !== 0) {
+    		check = true;
+    	}
+    	
+    	if (check === true) {
+	    	AIRTIME.button.enableButton("library_group_add");
+	    }
+	    else {
+	    	AIRTIME.button.disableButton("library_group_add");
+	    }
+    };
+	
 	mod.fnRowCallback = function( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
 		var $nRow = $(nRow);
 		
@@ -21,7 +39,6 @@ var AIRTIME = (function(AIRTIME){
 		$('#library_display tr:not(:first)').draggable({
 			helper: function(){
 			    var selected = $('#library_display tr:not(:first) input:checked').parents('tr'),
-			    	aItems = [],
 			    	container,
 			    	thead = $("#show_builder_table thead"),
 			    	colspan = thead.find("th").length,
@@ -34,10 +51,10 @@ var AIRTIME = (function(AIRTIME){
 			    }
 			    
 			    if (selected.length === 1) {
-			    	message = "Moving "+selected.length+" Item."
+			    	message = "Moving "+selected.length+" Item.";
 			    }
 			    else {
-			    	message = "Moving "+selected.length+" Items."
+			    	message = "Moving "+selected.length+" Items.";
 			    }
 			    
 			    container = $('<div/>').attr('id', 'draggingContainer')
@@ -61,8 +78,6 @@ var AIRTIME = (function(AIRTIME){
 	
 	mod.setupLibraryToolbar = function(oLibTable) {
 		var aButtons,
-			fnTest,
-			fnResetCol,
 			fnAddSelectedItems,
 		
 		fnAddSelectedItems = function() {
@@ -75,7 +90,7 @@ var AIRTIME = (function(AIRTIME){
 				aSchedIds = [];
 			
 			//process selected files/playlists.
-			for (i=0, length = aData.length; i < length; i++) {
+			for (i = 0, length = aData.length; i < length; i++) {
 				temp = aData[i];
 				aMediaIds.push({"id": temp.id, "type": temp.ftype});	
 			}
@@ -93,12 +108,13 @@ var AIRTIME = (function(AIRTIME){
 			
 			AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds);	
 		};
+		
 		//[0] = button text
 		//[1] = id 
 		//[2] = enabled
 		//[3] = click event
-		aButtons = [["Delete", "library_group_delete", true, AIRTIME.library.fnDeleteSelectedItems], 
-		           ["Add", "library_group_add", true, fnAddSelectedItems]];
+		aButtons = [["Delete", "library_group_delete", false, AIRTIME.library.fnDeleteSelectedItems], 
+		           ["Add", "library_group_add", false, fnAddSelectedItems]];
 		
 		addToolBarButtonsLibrary(aButtons);
 	};
diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js
index 626de54c0..42aa267ef 100644
--- a/airtime_mvc/public/js/airtime/library/library.js
+++ b/airtime_mvc/public/js/airtime/library/library.js
@@ -1,5 +1,6 @@
 var AIRTIME = (function(AIRTIME){
-	var mod;
+	var mod,
+		libraryInit;
 	
 	if (AIRTIME.library === undefined) {
 		AIRTIME.library = {};
@@ -13,6 +14,10 @@ var AIRTIME = (function(AIRTIME){
 		$.post("/library/delete", 
 			{"format": "json", "media": aMedia}, 
 			function(json){
+				if (json.message !== undefined) {
+					alert(json.message);
+				}
+				
 				oLibTT.fnSelectNone();
 				oLibTable.fnDraw();
 			});
@@ -36,6 +41,408 @@ var AIRTIME = (function(AIRTIME){
 		AIRTIME.library.fnDeleteItems(aMedia);
 	};
 	
+	libraryInit = function() {
+	    var oTable;
+	    
+	    oTable = $('#library_display').dataTable( {
+	    	
+	    	"aoColumns": [
+	          /* Checkbox */      {"sTitle": "<input type='checkbox' name='pl_cb_all'>", "mDataProp": "checkbox", "bSortable": false, "bSearchable": false, "sWidth": "25px", "sClass": "library_checkbox"},
+	          /* Type */          {"sTitle": "", "mDataProp": "image", "bSearchable": false, "sWidth": "25px", "sClass": "library_type", "iDataSort": 2},
+	          /* ftype */         {"sTitle": "", "mDataProp": "ftype", "bSearchable": false, "bVisible": false},
+	          /* Title */         {"sTitle": "Title", "mDataProp": "track_title", "sClass": "library_title"},
+	          /* Creator */       {"sTitle": "Creator", "mDataProp": "artist_name", "sClass": "library_creator"},
+	          /* Album */         {"sTitle": "Album", "mDataProp": "album_title", "sClass": "library_album"},
+	          /* Genre */         {"sTitle": "Genre", "mDataProp": "genre", "sClass": "library_genre"},
+	          /* Year */          {"sTitle": "Year", "mDataProp": "year", "sClass": "library_year", "sWidth": "60px"},
+	          /* Length */        {"sTitle": "Length", "mDataProp": "length", "sClass": "library_length", "sWidth": "80px"},
+	          /* Upload Time */   {"sTitle": "Uploaded", "mDataProp": "utime", "sClass": "library_upload_time"},
+	          /* Last Modified */ {"sTitle": "Last Modified", "mDataProp": "mtime", "bVisible": false, "sClass": "library_modified_time"},
+	          /* Track Number */  {"sTitle": "Track", "mDataProp": "track_number", "bSearchable": false, "bVisible": false, "sClass": "library_track"},
+	          /* Mood */  		  {"sTitle": "Mood", "mDataProp": "mood", "bSearchable": false, "bVisible": false, "sClass": "library_mood"},
+	          /* BPM */  {"sTitle": "BPM", "mDataProp": "bpm", "bSearchable": false, "bVisible": false, "sClass": "library_bpm"},
+	          /* Composer */  {"sTitle": "Composer", "mDataProp": "composer", "bSearchable": false, "bVisible": false, "sClass": "library_composer"},
+	          /* Website */  {"sTitle": "Website", "mDataProp": "info_url", "bSearchable": false, "bVisible": false, "sClass": "library_url"},
+	          /* Bit Rate */  {"sTitle": "Bit Rate", "mDataProp": "bit_rate", "bSearchable": false, "bVisible": false, "sClass": "library_bitrate", "sWidth": "80px"},
+	          /* Sample Rate */  {"sTitle": "Sample", "mDataProp": "sample_rate", "bSearchable": false, "bVisible": false, "sClass": "library_sr", "sWidth": "80px"},
+	          /* ISRC Number */  {"sTitle": "ISRC", "mDataProp": "isrc_number", "bSearchable": false, "bVisible": false, "sClass": "library_isrc"},
+	          /* Encoded */  {"sTitle": "Encoded", "mDataProp": "encoded_by", "bSearchable": false, "bVisible": false, "sClass": "library_encoded"},
+	          /* Label */  {"sTitle": "Label", "mDataProp": "label", "bSearchable": false, "bVisible": false, "sClass": "library_label"},
+	          /* Copyright */  {"sTitle": "Copyright", "mDataProp": "copyright", "bSearchable": false, "bVisible": false, "sClass": "library_copyright"},
+	          /* Mime */  {"sTitle": "Mime", "mDataProp": "mime", "bSearchable": false, "bVisible": false, "sClass": "library_mime"},
+	          /* Language */  {"sTitle": "Language", "mDataProp": "language", "bSearchable": false, "bVisible": false, "sClass": "library_language"}
+	          ],
+	    	              
+			"bProcessing": true,
+			"bServerSide": true,
+			
+			"bStateSave": true,
+			"fnStateSaveParams": function (oSettings, oData) {
+	    		//remove oData components we don't want to save.
+	    		delete oData.oSearch;
+	    		delete oData.aoSearchCols;
+		    },
+	        "fnStateSave": function (oSettings, oData) {
+	           
+	    		$.ajax({
+				  url: "/usersettings/set-library-datatable",
+				  type: "POST",
+				  data: {settings : oData, format: "json"},
+				  dataType: "json",
+				  success: function(){},
+				  error: function (jqXHR, textStatus, errorThrown) {
+					  var x;
+				  }
+				});
+	        },
+	        "fnStateLoad": function (oSettings) {
+	        	var o;
+
+	        	$.ajax({
+	  			  url: "/usersettings/get-library-datatable",
+	  			  type: "GET",
+	  			  data: {format: "json"},
+	  			  dataType: "json",
+	  			  async: false,
+	  			  success: function(json){
+	  				  o = json.settings;
+	  			  },
+	  			  error: function (jqXHR, textStatus, errorThrown) {
+					  var x;
+				  }
+	  			});
+	        	
+	        	return o;
+	        },
+	        "fnStateLoadParams": function (oSettings, oData) {
+	        	var i,
+					length,
+					a = oData.abVisCols;
+			
+	        	//putting serialized data back into the correct js type to make
+	        	//sure everything works properly.
+		        for (i = 0, length = a.length; i < length; i++) {	
+		        	a[i] = (a[i] === "true") ? true : false;
+		        }
+		        
+		        a = oData.ColReorder;
+		        for (i = 0, length = a.length; i < length; i++) {	
+		        	a[i] = parseInt(a[i], 10);
+		        }
+		       
+		        oData.iEnd = parseInt(oData.iEnd, 10);
+		        oData.iLength = parseInt(oData.iLength, 10);
+		        oData.iStart = parseInt(oData.iStart, 10);
+		        oData.iCreate = parseInt(oData.iCreate, 10);
+	        },
+			
+			"sAjaxSource": "/Library/contents",
+			"fnServerData": function ( sSource, aoData, fnCallback ) {
+	        	var type;
+	        	
+	    		aoData.push( { name: "format", value: "json"} );
+	    		
+	    		//push whether to search files/playlists or all.
+	    		type = $("#library_display_type").find("select").val();
+	    		type = (type === undefined) ? 0 : type;
+	    		aoData.push( { name: "type", value: type} );
+	    		
+				$.ajax( {
+					"dataType": 'json',
+					"type": "GET",
+					"url": sSource,
+					"data": aoData,
+					"success": fnCallback
+				} );
+			},
+			"fnRowCallback": AIRTIME.library.events.fnRowCallback,
+			"fnCreatedRow": function( nRow, aData, iDataIndex ) {
+				
+				//call the context menu so we can prevent the event from propagating.
+				$(nRow).find('td:not(.library_checkbox)').click(function(e){
+					
+					$(this).contextMenu({x: e.pageX, y: e.pageY});
+					
+					return false;
+				});
+
+				//add a tool tip to appear when the user clicks on the type icon.
+				$(nRow).find("td:not(:first, td>img)").qtip({
+					content: {
+						text: "Loading...",
+						title: {
+							text: aData.track_title
+						},
+						ajax: {
+							url: "/Library/get-file-meta-data",
+							type: "get",
+							data: ({format: "html", id : aData.id, type: aData.ftype}),
+							success: function(data, status) {
+								this.set('content.text', data);
+							}
+						}
+					},
+					position: {
+						target: 'event',
+						adjust: {
+							resize: true,
+							method: "flip flip"
+						},
+						my: 'left center',
+			            at: 'right center',
+			            viewport: $(window), // Keep the tooltip on-screen at all times
+			            effect: false // Disable positioning animation
+			        },
+					style: {
+						classes: "ui-tooltip-dark"
+					},
+					show: 'mousedown',
+				    events: {
+				       show: function(event, api) {
+				         // Only show the tooltip if it was a right-click
+				         if(event.originalEvent.button !== 2) {
+				            event.preventDefault();
+				         }
+				       }
+				   },
+					hide: 'mouseout'
+					
+				});
+			},
+			"fnDrawCallback": AIRTIME.library.events.fnDrawCallback,
+			"fnHeaderCallback": function(nHead) {
+				$(nHead).find("input[type=checkbox]").attr("checked", false);
+			},
+			
+			"aaSorting": [[3, 'asc']],
+			"sPaginationType": "full_numbers",
+			"bJQueryUI": true,
+			"bAutoWidth": false,
+	        "oLanguage": {
+	            "sSearch": ""
+	        },
+	       
+	        // R = ColReorder, C = ColVis, T = TableTools
+	        "sDom": 'Rl<"#library_display_type">fr<"H"T<"library_toolbar"C>>t<"F"ip>',
+	        
+	        "oTableTools": {
+	        	"sRowSelect": "multi",
+				"aButtons": [],
+				"fnRowSelected": function ( node ) {
+					var selected;
+	                    
+	                //seems to happen if everything is selected
+	                if ( node === null) {
+	                	selected = oTable.find("input[type=checkbox]");
+	                	selected.attr("checked", true);
+	                }
+	                else {
+	                	$(node).find("input[type=checkbox]").attr("checked", true);
+	                	selected = oTable.find("input[type=checkbox]").filter(":checked");
+	                }
+	                
+	                //checking to enable buttons
+	                AIRTIME.button.enableButton("library_group_delete");
+	                AIRTIME.library.events.enableAddButtonCheck();
+	            },
+	            "fnRowDeselected": function ( node ) {
+	            	var selected;
+	             
+	              //seems to happen if everything is deselected
+	                if ( node === null) {
+	                	oTable.find("input[type=checkbox]").attr("checked", false);
+	                	selected = [];
+	                }
+	                else {
+	                	$(node).find("input[type=checkbox]").attr("checked", false);
+	                	selected = oTable.find("input[type=checkbox]").filter(":checked");
+	                }
+	                
+	                //checking to disable buttons
+	                if (selected.length === 0) {
+	                	AIRTIME.button.disableButton("library_group_delete");
+	                }
+	                AIRTIME.library.events.enableAddButtonCheck();
+	            }
+			},
+			
+	        "oColVis": {
+	            "buttonText": "Show/Hide Columns",
+	            "sAlign": "right",
+	            "aiExclude": [0, 1, 2],
+	            "sSize": "css"
+			},
+			
+			"oColReorder": {
+				"iFixedColumns": 2
+			}
+			
+	    });
+	    oTable.fnSetFilteringDelay(350);
+	    
+	    AIRTIME.library.events.setupLibraryToolbar(oTable);
+	    
+	    $("#library_display_type")
+	    	.addClass("dataTables_type")
+	    	.append('<select name="library_display_type" />')
+	    	.find("select")
+	    		.append('<option value="0">All</option>')
+	    		.append('<option value="1">Files</option>')
+	    		.append('<option value="2">Playlists</option>')
+	    		.end()
+	    	.change(function(ev){
+	    		oTable.fnDraw();
+	    	});
+	      
+	    $('[name="pl_cb_all"]').click(function(){
+	    	var oTT = TableTools.fnGetInstance('library_display');
+	    	
+	    	if ($(this).is(":checked")) {
+	    		oTT.fnSelectAll();
+	    	}
+	    	else {
+	    		oTT.fnSelectNone();
+	    	}       
+	    });
+	    
+	    checkImportStatus();
+	    setInterval( checkImportStatus, 5000 );
+	    setInterval( checkSCUploadStatus, 5000 );
+	    
+	    addQtipToSCIcons();
+
+	    $.contextMenu({
+	        selector: '#library_display td:not(.library_checkbox)',
+	        trigger: "left",
+	        ignoreRightClick: true,
+	        
+	        build: function($el, e) {
+	    		var data, screen, items, callback, $tr;
+	    		
+	    		$tr = $el.parent();
+	    		data = $tr.data("aData");
+	    		screen = $tr.data("screen");
+	    		
+	    		function processMenuItems(oItems) {
+	    			
+	    			//define an add to playlist callback.
+	    			if (oItems.pl_add !== undefined) {
+	    				
+	    				callback = function() {
+	    					AIRTIME.playlist.fnAddItems([data.id], undefined, 'after');
+						};
+		    			
+	    				oItems.pl_add.callback = callback;
+	    			}
+	    			
+	    			//define an edit callback.
+	    			if (oItems.edit !== undefined) {
+	    				
+	    				if (data.ftype === "audioclip") {
+		    				callback = function() {
+		    					document.location.href = oItems.edit.url;
+							};
+	    				}
+		    			else {
+		    				callback = function() {
+		    					AIRTIME.playlist.fnEdit(data.id);
+							};
+		    			}
+	    				oItems.edit.callback = callback;
+	    			}
+	    			
+	    			//define a delete callback.
+	    			if (oItems.del !== undefined) {
+	    				
+	    				//delete through the playlist controller, will reset
+	    				//playlist screen if this is the currently edited playlist.
+	    				if (data.ftype === "playlist" && screen === "playlist") {
+	    					callback = function() {
+	        					
+	        					if (confirm('Are you sure you want to delete the selected item?')) {
+	        						AIRTIME.playlist.fnDelete(data.id);
+	        				    }
+	    					};
+	    				}
+	    				else {
+	    					callback = function() {
+	        					var media = [];
+	        					
+	        					if (confirm('Are you sure you want to delete the selected item?')) {
+	        						
+	        						media.push({"id": data.id, "type": data.ftype});
+	        						$.post(oItems.del.url, {format: "json", media: media }, function(json){
+	        							var oTable;
+	        							
+	        							if (json.message) {
+	        								alert(json.message);
+	        							}
+	        							
+	        							oTable = $("#library_display").dataTable();
+	            						oTable.fnDeleteRow( $tr[0] );
+	        						});
+	        				    }
+	    					};
+	    				}
+	    				
+	    				oItems.del.callback = callback;
+	    			}
+	    			
+	    			//define a download callback.
+	    			if (oItems.download !== undefined) {
+	    				
+	    				callback = function() {
+	    					document.location.href = oItems.download.url;
+						};
+	    				oItems.download.callback = callback;
+	    			}
+	    			//add callbacks for Soundcloud menu items.
+	    			if (oItems.soundcloud !== undefined) {
+	    				var soundcloud = oItems.soundcloud.items;
+	    				
+	    				//define an upload to soundcloud callback.
+	    				if (soundcloud.upload !== undefined) {
+	    					
+	    					callback = function() {
+		    					$.post(soundcloud.upload.url, function(){
+		    						addProgressIcon(data.id);
+		    					});
+	    					};
+	    					soundcloud.upload.callback = callback;
+	    				}
+	    				
+	    				//define a view on soundcloud callback
+	    				if (soundcloud.view !== undefined) {
+	    					
+	    					callback = function() {
+		    					window.open(soundcloud.view.url);
+	    					};
+	    					soundcloud.view.callback = callback;
+	    				}
+	    			}
+	    		
+	    			items = oItems;
+	    		}
+	    		
+	    		request = $.ajax({
+				  url: "/library/context-menu",
+				  type: "GET",
+				  data: {id : data.id, type: data.ftype, format: "json", "screen": screen},
+				  dataType: "json",
+				  async: false,
+				  success: function(json){
+					  processMenuItems(json.items);
+				  }
+				});
+
+	            return {
+	                items: items
+	            };
+	        }
+	    });
+	};
+	mod.libraryInit = libraryInit;
+	
 	return AIRTIME;
 	
 }(AIRTIME || {}));
@@ -43,42 +450,45 @@ var AIRTIME = (function(AIRTIME){
 function addToolBarButtonsLibrary(aButtons) {
 	var i,
 		length = aButtons.length,
-		libToolBar,
+		libToolBar = $(".library_toolbar"),
 		html,
 		buttonClass = '',
 		DEFAULT_CLASS = 'ui-button ui-state-default',
-		DISABLED_CLASS = 'ui-state-disabled';
+		DISABLED_CLASS = 'ui-state-disabled',
+		fn;
 	
-	libToolBar = $(".library_toolbar");
-	
-	for ( i=0; i < length; i+=1 ) {
+	for ( i = 0; i < length; i += 1 ) {
 		buttonClass = '';
 		
 		//add disabled class if not enabled.
 		if (aButtons[i][2] === false) {
-			buttonClass+=DISABLED_CLASS;
+			buttonClass += DISABLED_CLASS;
 		}
 		
-		html = '<div id="'+aButtons[i][1]+'" class="ColVis TableTools"><button class="'+DEFAULT_CLASS+' '+buttonClass+'"><span>'+aButtons[i][0]+'</span></button></div>';	
+		html = '<div class="ColVis TableTools '+aButtons[i][1]+'"><button class="'+DEFAULT_CLASS+' '+buttonClass+'"><span>'+aButtons[i][0]+'</span></button></div>';
 		libToolBar.append(html);
-		libToolBar.find("#"+aButtons[i][1]).click(aButtons[i][3]);
+		
+		//create a closure to preserve the state of i.
+		(function(index){
+			
+			libToolBar.find("."+aButtons[index][1]).click(function(){
+				fn = function() {
+					var $button = $(this).find("button");
+					
+					//only call the passed function if the button is enabled.
+					if (!$button.hasClass(DISABLED_CLASS)) {
+						aButtons[index][3]();
+					}	
+				};
+				
+				fn.call(this);
+			});
+			
+		}(i));
+			
 	}
 }
 
-function enableGroupBtn(btnId, func) {
-    btnId = '#' + btnId;
-    if ($(btnId).hasClass('ui-state-disabled')) {
-        $(btnId).removeClass('ui-state-disabled');
-    }
-}
-
-function disableGroupBtn(btnId) {
-    btnId = '#' + btnId;
-    if (!$(btnId).hasClass('ui-state-disabled')) {
-        $(btnId).addClass('ui-state-disabled');
-    }
-}
-
 function checkImportStatus(){
     $.getJSON('/Preference/is-import-in-progress', function(data){
         var div = $('#import_status');
@@ -210,389 +620,4 @@ function addQtipToSCIcons(){
             });
         }
     });
-}
-
-$(document).ready(function() {
-    var oTable;
-    
-    oTable = $('#library_display').dataTable( {
-    	
-    	"aoColumns": [
-          /* Checkbox */      {"sTitle": "<input type='checkbox' name='pl_cb_all'>", "mDataProp": "checkbox", "bSortable": false, "bSearchable": false, "sWidth": "25px", "sClass": "library_checkbox"},
-          /* Type */          {"sTitle": "", "mDataProp": "image", "bSearchable": false, "sWidth": "25px", "sClass": "library_type", "iDataSort": 2},
-          /* ftype */         {"sTitle": "", "mDataProp": "ftype", "bSearchable": false, "bVisible": false},
-          /* Title */         {"sTitle": "Title", "mDataProp": "track_title", "sClass": "library_title"},
-          /* Creator */       {"sTitle": "Creator", "mDataProp": "artist_name", "sClass": "library_creator"},
-          /* Album */         {"sTitle": "Album", "mDataProp": "album_title", "sClass": "library_album"},
-          /* Genre */         {"sTitle": "Genre", "mDataProp": "genre", "sClass": "library_genre"},
-          /* Year */          {"sTitle": "Year", "mDataProp": "year", "sClass": "library_year", "sWidth": "60px"},
-          /* Length */        {"sTitle": "Length", "mDataProp": "length", "sClass": "library_length", "sWidth": "80px"},
-          /* Upload Time */   {"sTitle": "Uploaded", "mDataProp": "utime", "sClass": "library_upload_time"},
-          /* Last Modified */ {"sTitle": "Last Modified", "mDataProp": "mtime", "bVisible": false, "sClass": "library_modified_time"},
-          /* Track Number */  {"sTitle": "Track", "mDataProp": "track_number", "bSearchable": false, "bVisible": false, "sClass": "library_track"},
-          /* Mood */  		  {"sTitle": "Mood", "mDataProp": "mood", "bSearchable": false, "bVisible": false, "sClass": "library_mood"},
-          /* BPM */  {"sTitle": "BPM", "mDataProp": "bpm", "bSearchable": false, "bVisible": false, "sClass": "library_bpm"},
-          /* Composer */  {"sTitle": "Composer", "mDataProp": "composer", "bSearchable": false, "bVisible": false, "sClass": "library_composer"},
-          /* Website */  {"sTitle": "Website", "mDataProp": "info_url", "bSearchable": false, "bVisible": false, "sClass": "library_url"},
-          /* Bit Rate */  {"sTitle": "Bit Rate", "mDataProp": "bit_rate", "bSearchable": false, "bVisible": false, "sClass": "library_bitrate"},
-          /* Sameple Rate */  {"sTitle": "Sample Rate", "mDataProp": "sample_rate", "bSearchable": false, "bVisible": false, "sClass": "library_sr"},
-          /* ISRC Number */  {"sTitle": "ISRC", "mDataProp": "isrc_number", "bSearchable": false, "bVisible": false, "sClass": "library_isrc"},
-          /* Encoded */  {"sTitle": "Encoded", "mDataProp": "encoded_by", "bSearchable": false, "bVisible": false, "sClass": "library_encoded"},
-          /* Label */  {"sTitle": "Label", "mDataProp": "label", "bSearchable": false, "bVisible": false, "sClass": "library_label"},
-          /* Copyright */  {"sTitle": "Copyright", "mDataProp": "copyright", "bSearchable": false, "bVisible": false, "sClass": "library_copyright"},
-          /* Mime */  {"sTitle": "Mime", "mDataProp": "mime", "bSearchable": false, "bVisible": false, "sClass": "library_mime"},
-          /* Language */  {"sTitle": "Language", "mDataProp": "language", "bSearchable": false, "bVisible": false, "sClass": "library_language"}
-          ],
-    	              
-		"bProcessing": true,
-		"bServerSide": true,
-		
-		"bStateSave": true,
-		"fnStateSaveParams": function (oSettings, oData) {
-    		//remove oData components we don't want to save.
-    		delete oData.oSearch;
-    		delete oData.aoSearchCols;
-	    },
-        "fnStateSave": function (oSettings, oData) {
-           
-    		$.ajax({
-			  url: "/usersettings/set-library-datatable",
-			  type: "POST",
-			  data: {settings : oData, format: "json"},
-			  dataType: "json",
-			  success: function(){},
-			  error: function (jqXHR, textStatus, errorThrown) {
-				  var x;
-			  }
-			});
-        },
-        "fnStateLoad": function (oSettings) {
-        	var o;
-
-        	$.ajax({
-  			  url: "/usersettings/get-library-datatable",
-  			  type: "GET",
-  			  data: {format: "json"},
-  			  dataType: "json",
-  			  async: false,
-  			  success: function(json){
-  				  o = json.settings;
-  			  },
-  			  error: function (jqXHR, textStatus, errorThrown) {
-				  var x;
-			  }
-  			});
-        	
-        	return o;
-        },
-        "fnStateLoadParams": function (oSettings, oData) {
-        	var i,
-				length,
-				a = oData.abVisCols;
-		
-        	//putting serialized data back into the correct js type to make
-        	//sure everything works properly.
-	        for (i = 0, length = a.length; i < length; i++) {	
-	        	a[i] = (a[i] === "true") ? true : false;
-	        }
-	        
-	        a = oData.ColReorder;
-	        for (i = 0, length = a.length; i < length; i++) {	
-	        	a[i] = parseInt(a[i], 10);
-	        }
-	       
-	        oData.iEnd = parseInt(oData.iEnd, 10);
-	        oData.iLength = parseInt(oData.iLength, 10);
-	        oData.iStart = parseInt(oData.iStart, 10);
-	        oData.iCreate = parseInt(oData.iCreate, 10);
-        },
-		
-		"sAjaxSource": "/Library/contents",
-		"fnServerData": function ( sSource, aoData, fnCallback ) {
-        	var type;
-        	
-    		aoData.push( { name: "format", value: "json"} );
-    		
-    		//push whether to search files/playlists or all.
-    		type = $("#library_display_type").find("select").val();
-    		type = (type === undefined) ? 0 : type;
-    		aoData.push( { name: "type", value: type} );
-    		
-			$.ajax( {
-				"dataType": 'json',
-				"type": "GET",
-				"url": sSource,
-				"data": aoData,
-				"success": fnCallback
-			} );
-		},
-		"fnRowCallback": AIRTIME.library.events.fnRowCallback,
-		"fnCreatedRow": function( nRow, aData, iDataIndex ) {
-			
-			//call the context menu so we can prevent the event from propagating.
-			$(nRow).find('td:not(.library_checkbox)').click(function(e){
-				
-				$(this).contextMenu({x: e.pageX, y: e.pageY});
-				
-				return false;
-			});
-
-			//add a tool tip to appear when the user clicks on the type icon.
-			$(nRow).find("td:not(:first, td>img)").qtip({
-				content: {
-					text: "Loading...",
-					title: {
-						text: aData.track_title
-					},
-					ajax: {
-						url: "/Library/get-file-meta-data",
-						type: "get",
-						data: ({format: "html", id : aData.id, type: aData.ftype}),
-						success: function(data, status) {
-							this.set('content.text', data);
-						}
-					}
-				},
-				position: {
-					target: 'event',
-					adjust: {
-						resize: true,
-						method: "flip flip"
-					},
-					my: 'left center',
-		            at: 'right center',
-		            viewport: $(window), // Keep the tooltip on-screen at all times
-		            effect: false // Disable positioning animation
-		        },
-				style: {
-					classes: "ui-tooltip-dark"
-				},
-				show: 'mousedown',
-			    events: {
-			       show: function(event, api) {
-			         // Only show the tooltip if it was a right-click
-			         if(event.originalEvent.button !== 2) {
-			            event.preventDefault();
-			         }
-			       }
-			   },
-				hide: 'mouseout'
-				
-			});
-		},
-		"fnDrawCallback": AIRTIME.library.events.fnDrawCallback,
-		"fnHeaderCallback": function(nHead) {
-			$(nHead).find("input[type=checkbox]").attr("checked", false);
-		},
-		
-		"aaSorting": [[3, 'asc']],
-		"sPaginationType": "full_numbers",
-		"bJQueryUI": true,
-		"bAutoWidth": false,
-        "oLanguage": {
-            "sSearch": ""
-        },
-       
-        // R = ColReorder, C = ColVis, T = TableTools
-        "sDom": 'Rl<"#library_display_type">fr<"H"T<"library_toolbar"C>>t<"F"ip>',
-        
-        "oTableTools": {
-        	"sRowSelect": "multi",
-			"aButtons": [],
-			"fnRowSelected": function ( node ) {
-                    
-                //seems to happen if everything is selected
-                if ( node === null) {
-                	oTable.find("input[type=checkbox]").attr("checked", true);
-                }
-                else {
-                	$(node).find("input[type=checkbox]").attr("checked", true);
-                }
-            },
-            "fnRowDeselected": function ( node ) {
-             
-              //seems to happen if everything is deselected
-                if ( node === null) {
-                	oTable.find("input[type=checkbox]").attr("checked", false);
-                }
-                else {
-                	$(node).find("input[type=checkbox]").attr("checked", false);
-                }
-            }
-		},
-		
-        "oColVis": {
-            "buttonText": "Show/Hide Columns",
-            "sAlign": "right",
-            "aiExclude": [0, 1, 2],
-            "sSize": "css"
-		},
-		
-		"oColReorder": {
-			"iFixedColumns": 2
-		}
-		
-    });
-    oTable.fnSetFilteringDelay(350);
-    
-    AIRTIME.library.events.setupLibraryToolbar(oTable);
-    
-    $("#library_display_type")
-    	.addClass("dataTables_type")
-    	.append('<select name="library_display_type" />')
-    	.find("select")
-    		.append('<option value="0">All</option>')
-    		.append('<option value="1">Files</option>')
-    		.append('<option value="2">Playlists</option>')
-    		.end()
-    	.change(function(ev){
-    		oTable.fnDraw();
-    	});
-      
-    $('[name="pl_cb_all"]').click(function(){
-    	var oTT = TableTools.fnGetInstance('library_display');
-    	
-    	if ($(this).is(":checked")) {
-    		oTT.fnSelectAll();
-    	}
-    	else {
-    		oTT.fnSelectNone();
-    	}       
-    });
-    
-    checkImportStatus();
-    setInterval( checkImportStatus, 5000 );
-    setInterval( checkSCUploadStatus, 5000 );
-    
-    addQtipToSCIcons();
-
-    $.contextMenu({
-        selector: '#library_display td:not(.library_checkbox)',
-        trigger: "left",
-        ignoreRightClick: true,
-        
-        build: function($el, e) {
-    		var x, request, data, screen, items, callback, $tr;
-    		
-    		$tr = $el.parent();
-    		data = $tr.data("aData");
-    		screen = $tr.data("screen");
-    		
-    		function processMenuItems(oItems) {
-    			
-    			//define an add to playlist callback.
-    			if (oItems.pl_add !== undefined) {
-    				
-    				callback = function() {
-    					AIRTIME.playlist.fnAddItems([data.id], undefined, 'after');
-					};
-	    			
-    				oItems.pl_add.callback = callback;
-    			}
-    			
-    			//define an edit callback.
-    			if (oItems.edit !== undefined) {
-    				
-    				if (data.ftype === "audioclip") {
-	    				callback = function() {
-	    					document.location.href = oItems.edit.url;
-						};
-    				}
-	    			else {
-	    				callback = function() {
-	    					AIRTIME.playlist.fnEdit(data.id);
-						};
-	    			}
-    				oItems.edit.callback = callback;
-    			}
-    			
-    			//define a delete callback.
-    			if (oItems.del !== undefined) {
-    				
-    				//delete through the playlist controller, will reset
-    				//playlist screen if this is the currently edited playlist.
-    				if (data.ftype === "playlist" && screen === "playlist") {
-    					callback = function() {
-        					
-        					if (confirm('Are you sure you want to delete the selected item?')) {
-        						AIRTIME.playlist.fnDelete(data.id);
-        				    }
-    					};
-    				}
-    				else {
-    					callback = function() {
-        					var media = [];
-        					
-        					if (confirm('Are you sure you want to delete the selected item?')) {
-        						
-        						media.push({"id": data.id, "type": data.ftype});
-        						$.post(oItems.del.url, {format: "json", media: media }, function(json){
-        							var oTable, tr;
-        							
-        							if (json.message) {
-        								alert(json.message);
-        							}
-        							
-        							oTable = $("#library_display").dataTable();
-            						oTable.fnDeleteRow( $tr[0] );
-        						});
-        				    }
-    					};
-    				}
-    				
-    				oItems.del.callback = callback;
-    			}
-    			
-    			//define a download callback.
-    			if (oItems.download !== undefined) {
-    				
-    				callback = function() {
-    					document.location.href = oItems.download.url;
-					};
-    				oItems.download.callback = callback;
-    			}
-    			//add callbacks for Soundcloud menu items.
-    			if (oItems.soundcloud !== undefined) {
-    				var soundcloud = oItems.soundcloud.items;
-    				
-    				//define an upload to soundcloud callback.
-    				if (soundcloud.upload !== undefined) {
-    					
-    					callback = function() {
-	    					$.post(soundcloud.upload.url, function(){
-	    						addProgressIcon(data.id);
-	    					});
-    					};
-    					soundcloud.upload.callback = callback;
-    				}
-    				
-    				//define a view on soundcloud callback
-    				if (soundcloud.view !== undefined) {
-    					
-    					callback = function() {
-	    					window.open(soundcloud.view.url);
-    					};
-    					soundcloud.view.callback = callback;
-    				}
-    			}
-    		
-    			items = oItems;
-    		}
-    		
-    		request = $.ajax({
-			  url: "/library/context-menu",
-			  type: "GET",
-			  data: {id : data.id, type: data.ftype, format: "json", "screen": screen},
-			  dataType: "json",
-			  async: false,
-			  success: function(json){
-				  processMenuItems(json.items);
-			  }
-			});
-
-            return {
-                items: items
-            };
-        }
-    });
-});
+}
\ No newline at end of file
diff --git a/airtime_mvc/public/js/airtime/library/main_library.js b/airtime_mvc/public/js/airtime/library/main_library.js
new file mode 100644
index 000000000..d50811e7e
--- /dev/null
+++ b/airtime_mvc/public/js/airtime/library/main_library.js
@@ -0,0 +1 @@
+$(document).ready(AIRTIME.library.libraryInit);
\ No newline at end of file
diff --git a/airtime_mvc/public/js/airtime/library/spl.js b/airtime_mvc/public/js/airtime/library/spl.js
index 4ca90ee9a..e90aeaa33 100644
--- a/airtime_mvc/public/js/airtime/library/spl.js
+++ b/airtime_mvc/public/js/airtime/library/spl.js
@@ -506,18 +506,25 @@ var AIRTIME = (function(AIRTIME){
 				fnUpdate;		
 			
 			fnReceive = function(event, ui) {
-				var selected = $('#library_display tr[id^="au"] input:checked').parents('tr'),
-					aItems = [];
-			
+				var aItems = [],
+					aSelected,
+					oLibTT = TableTools.fnGetInstance('library_display'),
+					i,
+					length;
+				
+				//filter out anything that isn't an audiofile.
+				aSelected = oLibTT.fnGetSelectedData();
 				//if nothing is checked select the dragged item.
-			    if (selected.length === 0) {
-			    	selected = ui.item;
+			    if (aSelected.length === 0) {
+			    	aSelected.push(ui.item.data("aData"));
 			    }
 			    
-			    selected.each(function(i, el) { 
-			    	aItems.push($(el).data("aData").id);
-			    });
-				
+				for (i = 0, length = aSelected.length; i < length; i++) {
+					if (aSelected[i].ftype === "audioclip") {
+						aItems.push(aSelected[i].id);
+					}
+				}
+	
 			    aReceiveItems = aItems;
 				html = ui.helper.html();
 			};
diff --git a/airtime_mvc/public/js/airtime/schedule/schedule.js b/airtime_mvc/public/js/airtime/schedule/schedule.js
index 8cd072b2c..e93be7df3 100644
--- a/airtime_mvc/public/js/airtime/schedule/schedule.js
+++ b/airtime_mvc/public/js/airtime/schedule/schedule.js
@@ -67,21 +67,10 @@ function uploadToSoundCloud(show_instance_id){
     }
 }
 
-function buildContentDialog (json){
-	var dialog = $(json.dialog),
-		viewportwidth,
-		viewportheight,
-		height,
-		width;
+function findViewportDimensions() {
+	var viewportwidth,
+		viewportheight;
 	
-    if (json.show_error == true){
-        alertShowErrorAndReload();
-    }
-	      
-	dialog.find("#show_progressbar").progressbar({
-		value: json.percentFilled
-	});
-        
 	// the more standards compliant browsers (mozilla/netscape/opera/IE7) use
 	// window.innerWidth and window.innerHeight
 	if (typeof window.innerWidth != 'undefined') {
@@ -101,9 +90,57 @@ function buildContentDialog (json){
 		viewportheight = document.getElementsByTagName('body')[0].clientHeight;
 	}
 	
-	height = viewportheight * 2/3;
-	width = viewportwidth * 4/5;
+	return {
+		width: viewportwidth,
+		height: viewportheight
+	};
+}
+
+function buildScheduleDialog (json) {
 	
+	var dialog = $(json.dialog),
+		viewport = findViewportDimensions(),
+		height = viewport.height * 0.96,
+		width = viewport.width * 0.96,
+		fnServer = AIRTIME.showbuilder.fnServerData;
+    
+	dialog.dialog({
+		autoOpen: false,
+		title: json.title,
+		width: width,
+		height: height,
+		modal: true,
+		close: closeDialog,
+		buttons: {"Ok": function() {
+			dialog.remove();
+			$("#schedule_calendar").fullCalendar( 'refetchEvents' );
+		}}
+	});
+	
+	//set the start end times so the builder datatables knows its time range.
+	fnServer.start = json.start;
+	fnServer.end = json.end;
+	
+	AIRTIME.library.libraryInit();
+	AIRTIME.showbuilder.builderDataTable();
+	
+	dialog.dialog('open');
+}
+
+function buildContentDialog (json){
+	var dialog = $(json.dialog),
+		viewport = findViewportDimensions(),
+		height = viewport.height * 2/3,
+		width = viewport.width * 4/5;
+	
+    if (json.show_error == true){
+        alertShowErrorAndReload();
+    }
+	      
+	dialog.find("#show_progressbar").progressbar({
+		value: json.percentFilled
+	});
+     
 	dialog.dialog({
 		autoOpen: false,
 		title: 'Show Contents',
@@ -201,8 +238,12 @@ $(document).ready(function() {
     			if (oItems.schedule !== undefined) {
     				
     				callback = function() {
-    					document.location = oItems.schedule.url + "from/" + data.startUnix + "/to/" + data.endUnix;
-					};
+    					
+    					$.post(oItems.schedule.url, {format: "json", id: data.id}, function(json){
+    						buildScheduleDialog(json);
+    					});
+    				};
+    				
     				oItems.schedule.callback = callback;
     			}
     			
diff --git a/airtime_mvc/public/js/airtime/showbuilder/builder.js b/airtime_mvc/public/js/airtime/showbuilder/builder.js
index 084fda9ff..f64d0914e 100644
--- a/airtime_mvc/public/js/airtime/showbuilder/builder.js
+++ b/airtime_mvc/public/js/airtime/showbuilder/builder.js
@@ -1,6 +1,7 @@
 var AIRTIME = (function(AIRTIME){
 	var mod,
-		oSchedTable;
+		oSchedTable,
+		fnServerData;
 	
 	if (AIRTIME.showbuilder === undefined) {
 		AIRTIME.showbuilder = {};
@@ -45,111 +46,6 @@ var AIRTIME = (function(AIRTIME){
 			});
 	};
 	
-	mod.init = function(oTable) {
-		oSchedTable = oTable;
-	};
-	
-	return AIRTIME;
-	
-}(AIRTIME || {}));
-
-
-$(document).ready(function() {
-	var tableDiv = $('#show_builder_table'),
-		oTable,
-		oBaseDatePickerSettings,
-		oBaseTimePickerSettings,
-		fnAddSelectedItems,
-		fnRemoveSelectedItems,
-		oRange,
-		fnServerData;
-	
-	oBaseDatePickerSettings = {
-		dateFormat: 'yy-mm-dd',
-		onSelect: function(sDate, oDatePicker) {
-			var oDate,
-				dInput;
-			
-			dInput = $(this);			
-			oDate = dInput.datepicker( "setDate", sDate );
-		}
-	};
-	
-	oBaseTimePickerSettings = {
-		showPeriodLabels: false,
-		showCloseButton: true,
-		showLeadingZero: false,
-		defaultTime: '0:00'
-	};
-	
-	/*
-	 * Get the schedule range start in unix timestamp form (in seconds).
-	 * defaults to NOW if nothing is selected.
-	 * 
-	 * @param String sDatePickerId
-	 * 
-	 * @param String sTimePickerId
-	 * 
-	 * @return Number iTime
-	 */
-	function fnGetTimestamp(sDatePickerId, sTimePickerId) {
-		var date, 
-			time,
-			iTime,
-			iServerOffset,
-			iClientOffset;
-	
-		if ($(sDatePickerId).val() === "") {
-			return 0;
-		}
-		
-		date = $(sDatePickerId).val();
-		time = $(sTimePickerId).val();
-		
-		date = date.split("-");
-		time = time.split(":");
-		
-		//0 based month in js.
-		oDate = new Date(date[0], date[1]-1, date[2], time[0], time[1]);
-		
-		iTime = oDate.getTime(); //value is in millisec.
-		iTime = Math.round(iTime / 1000);
-		iServerOffset = serverTimezoneOffset;
-		iClientOffset = oDate.getTimezoneOffset() * -60;//function returns minutes
-		
-		//adjust for the fact the the Date object is in client time.
-		iTime = iTime + iClientOffset + iServerOffset;
-		
-		return iTime;
-	}
-	/*
-	 * Returns an object containing a unix timestamp in seconds for the start/end range
-	 * 
-	 * @return Object {"start", "end", "range"}
-	 */
-	function fnGetScheduleRange() {
-		var iStart, 
-			iEnd, 
-			iRange,
-			DEFAULT_RANGE = 60*60*24;
-		
-		iStart = fnGetTimestamp("#sb_date_start", "#sb_time_start");
-		iEnd = fnGetTimestamp("#sb_date_end", "#sb_time_end");
-		
-		iRange = iEnd - iStart;
-		
-		if (iRange === 0 || iEnd < iStart) {
-			iEnd = iStart + DEFAULT_RANGE;
-			iRange = DEFAULT_RANGE;
-		}
-		
-		return {
-			start: iStart,
-			end: iEnd,
-			range: iRange
-		};
-	}
-
 	fnServerData = function ( sSource, aoData, fnCallback ) {
 		aoData.push( { name: "format", value: "json"} );
 		
@@ -173,428 +69,435 @@ $(document).ready(function() {
 		} );
 	};
 	
-	oRange = fnGetScheduleRange();	
-	fnServerData.start = oRange.start;
-	fnServerData.end = oRange.end;
-
-	fnRemoveSelectedItems = function() {
-		var oTT = TableTools.fnGetInstance('show_builder_table'),
-			aData = oTT.fnGetSelectedData(),
-			i,
-			length,
-			temp,
-			aItems = [];
+	mod.fnServerData = fnServerData;
 	
-		for (i=0, length = aData.length; i < length; i++) {
-			temp = aData[i];
-			aItems.push({"id": temp.id, "instance": temp.instance, "timestamp": temp.timestamp}); 	
-		}
-		
-		AIRTIME.showbuilder.fnRemove(aItems);
-	};
-	
-	oTable = tableDiv.dataTable( {
-		"aoColumns": [
-	    /* checkbox */ {"mDataProp": "allowed", "sTitle": "<input type='checkbox' name='sb_cb_all'>", "sWidth": "15px"},
-        /* starts */{"mDataProp": "starts", "sTitle": "Start"},
-        /* ends */{"mDataProp": "ends", "sTitle": "End"},
-        /* runtime */{"mDataProp": "runtime", "sTitle": "Duration", "sClass": "library_length"},
-        /* title */{"mDataProp": "title", "sTitle": "Title"},
-        /* creator */{"mDataProp": "creator", "sTitle": "Creator"},
-        /* album */{"mDataProp": "album", "sTitle": "Album"},
-        /* cue in */{"mDataProp": "cuein", "sTitle": "Cue In", "bVisible": false},
-        /* cue out */{"mDataProp": "cueout", "sTitle": "Cue Out", "bVisible": false},
-        /* fade in */{"mDataProp": "fadein", "sTitle": "Fade In", "bVisible": false},
-        /* fade out */{"mDataProp": "fadeout", "sTitle": "Fade Out", "bVisible": false}
-        ],
-        
-        "bJQueryUI": true,
-        "bSort": false,
-        "bFilter": false,
-        "bProcessing": true,
-		"bServerSide": true,
-		"bInfo": false,
-		"bAutoWidth": false,
-		
-		"bStateSave": true,
-		"fnStateSaveParams": function (oSettings, oData) {
-    		//remove oData components we don't want to save.
-    		delete oData.oSearch;
-    		delete oData.aoSearchCols;
-	    },
-        "fnStateSave": function (oSettings, oData) {
-           
-    		$.ajax({
-			  url: "/usersettings/set-timeline-datatable",
-			  type: "POST",
-			  data: {settings : oData, format: "json"},
-			  dataType: "json",
-			  success: function(){},
-			  error: function (jqXHR, textStatus, errorThrown) {
-				  var x;
-			  }
-			});
-        },
-        "fnStateLoad": function (oSettings) {
-        	var o;
+	mod.builderDataTable = function() {
+		var tableDiv = $('#show_builder_table'),
+			oTable,
+			fnRemoveSelectedItems;
 
-        	$.ajax({
-  			  url: "/usersettings/get-timeline-datatable",
-  			  type: "GET",
-  			  data: {format: "json"},
-  			  dataType: "json",
-  			  async: false,
-  			  success: function(json){
-  				  o = json.settings;
-  			  },
-  			  error: function (jqXHR, textStatus, errorThrown) {
-				  var x;
-			  }
-  			});
-        	
-        	return o;
-        },
-        "fnStateLoadParams": function (oSettings, oData) {
-        	var i,
+		fnRemoveSelectedItems = function() {
+			var oTT = TableTools.fnGetInstance('show_builder_table'),
+				aData = oTT.fnGetSelectedData(),
+				i,
 				length,
-				a = oData.abVisCols;
+				temp,
+				aItems = [];
 		
-        	//putting serialized data back into the correct js type to make
-        	//sure everything works properly.
-	        for (i = 0, length = a.length; i < length; i++) {	
-	        	a[i] = (a[i] === "true") ? true : false;
-	        }
+			for (i=0, length = aData.length; i < length; i++) {
+				temp = aData[i];
+				aItems.push({"id": temp.id, "instance": temp.instance, "timestamp": temp.timestamp}); 	
+			}
+			
+			AIRTIME.showbuilder.fnRemove(aItems);
+		};
+		
+		oTable = tableDiv.dataTable( {
+			"aoColumns": [
+		    /* checkbox */ {"mDataProp": "allowed", "sTitle": "<input type='checkbox' name='sb_cb_all'>", "sWidth": "15px"},
+	        /* starts */{"mDataProp": "starts", "sTitle": "Start"},
+	        /* ends */{"mDataProp": "ends", "sTitle": "End"},
+	        /* runtime */{"mDataProp": "runtime", "sTitle": "Duration", "sClass": "library_length"},
+	        /* title */{"mDataProp": "title", "sTitle": "Title"},
+	        /* creator */{"mDataProp": "creator", "sTitle": "Creator"},
+	        /* album */{"mDataProp": "album", "sTitle": "Album"},
+	        /* cue in */{"mDataProp": "cuein", "sTitle": "Cue In", "bVisible": false},
+	        /* cue out */{"mDataProp": "cueout", "sTitle": "Cue Out", "bVisible": false},
+	        /* fade in */{"mDataProp": "fadein", "sTitle": "Fade In", "bVisible": false},
+	        /* fade out */{"mDataProp": "fadeout", "sTitle": "Fade Out", "bVisible": false}
+	        ],
 	        
-	        a = oData.ColReorder;
-	        for (i = 0, length = a.length; i < length; i++) {	
-	        	a[i] = parseInt(a[i], 10);
-	        }
-	       
-	        oData.iCreate = parseInt(oData.iCreate, 10);
-        },
-        
-		"fnServerData": fnServerData,
-		"fnRowCallback": function ( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
-			var i,
-				sSeparatorHTML,
-				fnPrepareSeparatorRow,
-				node,
-				cl="";
+	        "bJQueryUI": true,
+	        "bSort": false,
+	        "bFilter": false,
+	        "bProcessing": true,
+			"bServerSide": true,
+			"bInfo": false,
+			"bAutoWidth": false,
 			
-			//save some info for reordering purposes.
-			$(nRow).data({"aData": aData});
+			"bStateSave": true,
+			"fnStateSaveParams": function (oSettings, oData) {
+	    		//remove oData components we don't want to save.
+	    		delete oData.oSearch;
+	    		delete oData.aoSearchCols;
+		    },
+	        "fnStateSave": function (oSettings, oData) {
+	           
+	    		$.ajax({
+				  url: "/usersettings/set-timeline-datatable",
+				  type: "POST",
+				  data: {settings : oData, format: "json"},
+				  dataType: "json",
+				  success: function(){},
+				  error: function (jqXHR, textStatus, errorThrown) {
+					  var x;
+				  }
+				});
+	        },
+	        "fnStateLoad": function (oSettings) {
+	        	var o;
+
+	        	$.ajax({
+	  			  url: "/usersettings/get-timeline-datatable",
+	  			  type: "GET",
+	  			  data: {format: "json"},
+	  			  dataType: "json",
+	  			  async: false,
+	  			  success: function(json){
+	  				  o = json.settings;
+	  			  },
+	  			  error: function (jqXHR, textStatus, errorThrown) {
+					  var x;
+				  }
+	  			});
+	        	
+	        	return o;
+	        },
+	        "fnStateLoadParams": function (oSettings, oData) {
+	        	var i,
+					length,
+					a = oData.abVisCols;
 			
-			if (aData.allowed !== true) {
-				$(nRow).addClass("sb-not-allowed");
-			}
-			
-			//status used to colour tracks.
-			if (aData.status === 1) {
-				$(nRow).addClass("sb-boundry");
-			}
-			else if (aData.status === 2) {
-				$(nRow).addClass("sb-over");
-			}
-			
-			fnPrepareSeparatorRow = function(sRowContent, sClass, iNodeIndex) {
+	        	//putting serialized data back into the correct js type to make
+	        	//sure everything works properly.
+		        for (i = 0, length = a.length; i < length; i++) {	
+		        	a[i] = (a[i] === "true") ? true : false;
+		        }
+		        
+		        a = oData.ColReorder;
+		        for (i = 0, length = a.length; i < length; i++) {	
+		        	a[i] = parseInt(a[i], 10);
+		        }
+		       
+		        oData.iCreate = parseInt(oData.iCreate, 10);
+	        },
+	        
+			"fnServerData": AIRTIME.showbuilder.fnServerData,
+			"fnRowCallback": function ( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
+				var i,
+					sSeparatorHTML,
+					fnPrepareSeparatorRow,
+					node,
+					cl="";
 				
-				node = nRow.children[iNodeIndex];
-				node.innerHTML = sRowContent;
-				node.setAttribute('colspan',100);
-				for (i = iNodeIndex + 1; i < nRow.children.length; i = i+1) {
-					node = nRow.children[i];
-					node.innerHTML = "";
-					node.setAttribute("style", "display : none");
+				//save some info for reordering purposes.
+				$(nRow).data({"aData": aData});
+				
+				if (aData.current === true) {
+					$(nRow).addClass("sb-now-playing");
 				}
 				
-				$(nRow).addClass(sClass);
+				if (aData.allowed !== true) {
+					$(nRow).addClass("sb-not-allowed");
+				}
+				else {
+					$(nRow).addClass("sb-allowed");
+				}
+				
+				//status used to colour tracks.
+				if (aData.status === 2) {
+					$(nRow).addClass("sb-boundry");
+				}
+				else if (aData.status === 0) {
+					$(nRow).addClass("sb-over");
+				}
+				
+				fnPrepareSeparatorRow = function(sRowContent, sClass, iNodeIndex) {
+					
+					node = nRow.children[iNodeIndex];
+					node.innerHTML = sRowContent;
+					node.setAttribute('colspan',100);
+					for (i = iNodeIndex + 1; i < nRow.children.length; i = i+1) {
+						node = nRow.children[i];
+						node.innerHTML = "";
+						node.setAttribute("style", "display : none");
+					}
+					
+					$(nRow).addClass(sClass);
+				};
+				
+				if (aData.header === true) {
+					cl = 'sb-header';
+					
+					sSeparatorHTML = '<span>'+aData.title+'</span><span>'+aData.starts+'</span><span>'+aData.ends+'</span>';
+					fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
+				}
+				else if (aData.footer === true) {
+					node = nRow.children[0];
+					cl = 'sb-footer';
+					
+					//check the show's content status.
+					if (aData.runtime > 0) {
+						node.innerHTML = '<span class="ui-icon ui-icon-check"></span>';
+						cl = cl + ' ui-state-highlight';
+					}
+					else {
+						node.innerHTML = '<span class="ui-icon ui-icon-notice"></span>';
+						cl = cl + ' ui-state-error';
+					}
+						
+					sSeparatorHTML = '<span>'+aData.fRuntime+'</span>';
+					fnPrepareSeparatorRow(sSeparatorHTML, cl, 1);
+				}
+				else if (aData.empty === true) {
+					
+					sSeparatorHTML = '<span>Show Empty</span>';
+					cl = cl + " sb-empty odd";
+					
+					fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
+				}
+				else if (aData.record === true) {
+					
+					sSeparatorHTML = '<span>Recording From Line In</span>';
+					cl = cl + " sb-record odd";
+					
+					fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
+				}
+				else {
+					
+					node = nRow.children[0];
+					if (aData.allowed === true) {
+						node.innerHTML = '<input type="checkbox" name="'+aData.id+'"></input>';
+					}
+					else {
+						node.innerHTML = '';
+					}
+				}
+			},
+			"fnDrawCallback": function(oSettings, json) {
+				var wrapperDiv,
+					markerDiv,
+					td;
+				
+				//create cursor arrows.
+				tableDiv.find("tr:not(:first, .sb-footer, .sb-empty, .sb-not-allowed)").each(function(i, el) {
+			    	td = $(el).find("td:first");
+			    	if (td.hasClass("dataTables_empty")) {
+			    		return false;
+			    	}
+			    	
+			    	wrapperDiv = $("<div />", {
+			    		"class": "innerWrapper",
+			    		"css": {
+			    			"height": td.height()
+			    		}
+			    	});
+			    	markerDiv = $("<div />", {
+			    		"class": "marker"
+			    	});
+			    	
+		    		td.append(markerDiv).wrapInner(wrapperDiv);
+			    });
+		    },
+			"fnHeaderCallback": function(nHead) {
+				$(nHead).find("input[type=checkbox]").attr("checked", false);
+			},
+			//remove any selected nodes before the draw.
+			"fnPreDrawCallback": function( oSettings ) {
+				var oTT = TableTools.fnGetInstance('show_builder_table');
+				oTT.fnSelectNone();
+		    },
+			
+			"oColVis": {
+				"aiExclude": [ 0, 1 ]
+			},
+			
+			"oColReorder": {
+				"iFixedColumns": 2
+			},
+			
+			"oTableTools": {
+	        	"sRowSelect": "multi",
+				"aButtons": [],
+				"fnPreRowSelect": function ( e ) {
+					var node = e.currentTarget;
+					//don't select separating rows, or shows without privileges.
+					if ($(node).hasClass("sb-header")
+						|| $(node).hasClass("sb-footer")
+						|| $(node).hasClass("sb-empty")
+						|| $(node).hasClass("sb-not-allowed")) {
+						return false;
+					}
+					return true;
+	            },
+				"fnRowSelected": function ( node ) {
+
+	                //seems to happen if everything is selected
+	                if ( node === null) {
+	                	oTable.find("input[type=checkbox]").attr("checked", true);
+	                }
+	                else {
+	                	$(node).find("input[type=checkbox]").attr("checked", true);
+	                }
+	                
+	                //checking to enable buttons
+	                AIRTIME.button.enableButton("sb_delete");
+	            },
+	            "fnRowDeselected": function ( node ) {
+	            	var selected;
+	            		       	
+	              //seems to happen if everything is deselected
+	                if ( node === null) {
+	                	tableDiv.find("input[type=checkbox]").attr("checked", false);
+	                	selected = [];
+	                }
+	                else {
+	                	$(node).find("input[type=checkbox]").attr("checked", false);
+	                	selected = tableDiv.find("input[type=checkbox]").filter(":checked");
+	                }
+	                
+	                //checking to disable buttons
+	                if (selected.length === 0) {
+	                	AIRTIME.button.disableButton("sb_delete");
+	                }
+	            }
+			},
+			
+	        // R = ColReorderResize, C = ColVis, T = TableTools
+	        "sDom": 'Rr<"H"CT>t<"F">',
+	        
+	        "sAjaxDataProp": "schedule",
+			"sAjaxSource": "/showbuilder/builder-feed"	
+		});
+		
+		$('[name="sb_cb_all"]').click(function(){
+	    	var oTT = TableTools.fnGetInstance('show_builder_table');
+	    	
+	    	if ($(this).is(":checked")) {
+	    		var allowedNodes;
+	    		
+	    		allowedNodes = oTable.find('tr:not(:first, .sb-header, .sb-empty, .sb-footer, .sb-not-allowed)');
+	    		
+	    		allowedNodes.each(function(i, el){
+	    			oTT.fnSelect(el);
+	    		});	
+	    	}
+	    	else {
+	    		oTT.fnSelectNone();
+	    	}       
+	    });
+		
+		var sortableConf = (function(){
+			var origTrs,
+				aItemData = [],
+				oPrevData,
+				fnAdd,
+				fnMove,
+				fnReceive,
+				fnUpdate,
+				i, 
+				html;
+			
+			fnAdd = function() {
+				var aMediaIds = [],
+					aSchedIds = [];
+				
+				for(i = 0; i < aItemData.length; i++) {
+					aMediaIds.push({"id": aItemData[i].id, "type": aItemData[i].ftype});
+				}
+				aSchedIds.push({"id": oPrevData.id, "instance": oPrevData.instance, "timestamp": oPrevData.timestamp});
+				
+				AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds);
 			};
 			
-			if (aData.header === true) {
-				cl = 'sb-header';
+			fnMove = function() {
+				var aSelect = [],
+					aAfter = [];
+			
+				aSelect.push({"id": aItemData[0].id, "instance": aItemData[0].instance, "timestamp": aItemData[0].timestamp});
+				aAfter.push({"id": oPrevData.id, "instance": oPrevData.instance, "timestamp": oPrevData.timestamp});
+		
+				AIRTIME.showbuilder.fnMove(aSelect, aAfter);
+			};
+			
+			fnReceive = function(event, ui) {
+				var aItems = [],
+					oLibTT = TableTools.fnGetInstance('library_display');
+			
+				aItems = oLibTT.fnGetSelectedData();
 				
-				sSeparatorHTML = '<span>'+aData.title+'</span><span>'+aData.starts+'</span><span>'+aData.ends+'</span>';
-				fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
-			}
-			else if (aData.footer === true) {
-				node = nRow.children[0];
-				cl = 'sb-footer';
+				//if nothing is checked select the dragged item.
+			    if (aItems.length === 0) {
+			    	aItems.push(ui.item.data("aData"));
+			    }
+			    
+				origTrs = aItems;
+				html = ui.helper.html();
+			};
+			
+			fnUpdate = function(event, ui) {
+				var prev = ui.item.prev();
 				
-				//check the show's content status.
-				if (aData.runtime > 0) {
-					node.innerHTML = '<span class="ui-icon ui-icon-check"></span>';
-					cl = cl + ' ui-state-highlight';
-				}
-				else {
-					node.innerHTML = '<span class="ui-icon ui-icon-notice"></span>';
-					cl = cl + ' ui-state-error';
+				//can't add items outside of shows.
+				if (!prev.hasClass("sb-allowed")) {
+					alert("Cannot schedule outside a show.");
+					ui.item.remove();
+					return;
 				}
+				
+				aItemData = [];
+				oPrevData = prev.data("aData");
+				
+				//item was dragged in
+				if (origTrs !== undefined) {
 					
-				sSeparatorHTML = '<span>'+aData.fRuntime+'</span>';
-				fnPrepareSeparatorRow(sSeparatorHTML, cl, 1);
-			}
-			else if (aData.empty === true) {
-				
-				sSeparatorHTML = '<span>Show Empty</span>';
-				cl = cl + " sb-empty odd";
-				
-				fnPrepareSeparatorRow(sSeparatorHTML, cl, 0);
-			}
-			else {
-				
-				node = nRow.children[0];
-				if (aData.allowed === true) {
-					node.innerHTML = '<input type="checkbox" name="'+aData.id+'"></input>';
+					$("#show_builder_table tr.ui-draggable")
+						.empty()
+						.after(html);
+					
+					aItemData = origTrs;
+					origTrs = undefined;
+					fnAdd();
 				}
+				//item was reordered.
 				else {
-					node.innerHTML = '';
+					aItemData.push(ui.item.data("aData"));
+					fnMove();
 				}
+			};
+			
+			return {
+				placeholder: "placeholder show-builder-placeholder ui-state-highlight",
+				forcePlaceholderSize: true,
+				items: 'tr:not(:first, :last, .sb-header, .sb-footer, .sb-not-allowed)',
+				receive: fnReceive,
+				update: fnUpdate
+			};
+		}());
+		
+		tableDiv.sortable(sortableConf);
+		
+		$("#show_builder .fg-toolbar")
+			.append('<div class="ColVis TableTools sb_delete"><button class="ui-button ui-state-default ui-state-disabled"><span>Delete</span></button></div>')
+			.click(fnRemoveSelectedItems);
+		
+		//set things like a reference to the table.
+		AIRTIME.showbuilder.init(oTable);
+		
+		//add event to cursors.
+		tableDiv.find("tbody").on("click", "div.marker", function(event){
+			var tr = $(this).parents("tr"),
+				cursorSelClass = "cursor-selected-row";
+			
+			if (tr.hasClass(cursorSelClass)) {
+				tr.removeClass(cursorSelClass);
 			}
-		},
-		"fnDrawCallback": function(oSettings, json) {
-			var wrapperDiv,
-				markerDiv,
-				td;
-			
-			//create cursor arrows.
-			tableDiv.find("tr:not(:first, .sb-footer, .sb-empty, .sb-not-allowed)").each(function(i, el) {
-		    	td = $(el).find("td:first");
-		    	if (td.hasClass("dataTables_empty")) {
-		    		return false;
-		    	}
-		    	
-		    	wrapperDiv = $("<div />", {
-		    		"class": "innerWrapper",
-		    		"css": {
-		    			"height": td.height()
-		    		}
-		    	});
-		    	markerDiv = $("<div />", {
-		    		"class": "marker"
-		    	});
-		    	
-	    		td.append(markerDiv).wrapInner(wrapperDiv);
-		    });
-	    },
-		"fnHeaderCallback": function(nHead) {
-			$(nHead).find("input[type=checkbox]").attr("checked", false);
-		},
-		//remove any selected nodes before the draw.
-		"fnPreDrawCallback": function( oSettings ) {
-			var oTT = TableTools.fnGetInstance('show_builder_table');
-			oTT.fnSelectNone();
-	    },
-		
-		"oColVis": {
-			"aiExclude": [ 0, 1 ]
-		},
-		
-		"oColReorder": {
-			"iFixedColumns": 2
-		},
-		
-		"oTableTools": {
-        	"sRowSelect": "multi",
-			"aButtons": [],
-			"fnPreRowSelect": function ( e ) {
-				var node = e.currentTarget;
-				//don't select separating rows, or shows without privileges.
-				if ($(node).hasClass("sb-header")
-					|| $(node).hasClass("sb-footer")
-					|| $(node).hasClass("sb-empty")
-					|| $(node).hasClass("sb-not-allowed")) {
-					return false;
-				}
-				return true;
-            },
-			"fnRowSelected": function ( node ) {
-
-                //seems to happen if everything is selected
-                if ( node === null) {
-                	oTable.find("input[type=checkbox]").attr("checked", true);
-                }
-                else {
-                	$(node).find("input[type=checkbox]").attr("checked", true);
-                }
-            },
-            "fnRowDeselected": function ( node ) {
-	
-              //seems to happen if everything is deselected
-                if ( node === null) {
-                	var oTable = $("#show_builder_table").dataTable();
-                	oTable.find("input[type=checkbox]").attr("checked", false);
-                }
-                else {
-                	$(node).find("input[type=checkbox]").attr("checked", false);
-                }
-            }
-		},
-		
-        // R = ColReorderResize, C = ColVis, T = TableTools
-        "sDom": 'Rr<"H"CT>t<"F">',
-        
-        "sAjaxDataProp": "schedule",
-		"sAjaxSource": "/showbuilder/builder-feed"	
-	});
-	
-	$('[name="sb_cb_all"]').click(function(){
-    	var oTT = TableTools.fnGetInstance('show_builder_table');
-    	
-    	if ($(this).is(":checked")) {
-    		var allowedNodes;
-    		
-    		allowedNodes = oTable.find('tr:not(:first, .sb-header, .sb-empty, .sb-footer, .sb-not-allowed)');
-    		
-    		allowedNodes.each(function(i, el){
-    			oTT.fnSelect(el);
-    		});	
-    	}
-    	else {
-    		oTT.fnSelectNone();
-    	}       
-    });
-	
-	$("#sb_date_start").datepicker(oBaseDatePickerSettings);
-	$("#sb_time_start").timepicker(oBaseTimePickerSettings);
-	$("#sb_date_end").datepicker(oBaseDatePickerSettings);
-	$("#sb_time_end").timepicker(oBaseTimePickerSettings);
-	
-	$("#sb_submit").click(function(ev){
-		var fn,
-			oRange,
-			op;
-		
-		oRange = fnGetScheduleRange();
-		
-	    fn = oTable.fnSettings().fnServerData;
-	    fn.start = oRange.start;
-	    fn.end = oRange.end;
-	    
-	    op = $("div.sb-advanced-options");
-	    if (op.is(":visible")) {
-	    	
-	    	if (fn.ops === undefined) {
-	    		fn.ops = {};
-	    	}
-	    	fn.ops.showFilter = op.find("#sb_show_filter").val();
-	    	fn.ops.myShows = op.find("#sb_my_shows").is(":checked") ? 1 : 0;
-	    }
-		
-		oTable.fnDraw();
-	});
-	
-	var sortableConf = (function(){
-		var origTrs,
-			aItemData = [],
-			oPrevData,
-			fnAdd,
-			fnMove,
-			fnReceive,
-			fnUpdate,
-			i, 
-			html;
-		
-		fnAdd = function() {
-			var aMediaIds = [],
-				aSchedIds = [],
-				oLibTT = TableTools.fnGetInstance('library_display');
-			
-			for(i=0; i < aItemData.length; i++) {
-				aMediaIds.push({"id": aItemData[i].id, "type": aItemData[i].ftype});
-			}
-			aSchedIds.push({"id": oPrevData.id, "instance": oPrevData.instance, "timestamp": oPrevData.timestamp});
-			
-			AIRTIME.showbuilder.fnAdd(aMediaIds, aSchedIds);
-		};
-		
-		fnMove = function() {
-			var aSelect = [],
-				aAfter = [];
-		
-			aSelect.push({"id": aItemData[0].id, "instance": aItemData[0].instance, "timestamp": aItemData[0].timestamp});
-			aAfter.push({"id": oPrevData.id, "instance": oPrevData.instance, "timestamp": oPrevData.timestamp});
-	
-			AIRTIME.showbuilder.fnMove(aSelect, aAfter);
-		};
-		
-		fnReceive = function(event, ui) {
-			var selected = $('#library_display tr:not(:first) input:checked').parents('tr'),
-				aItems = [];
-			
-			//if nothing is checked select the dragged item.
-		    if (selected.length === 0) {
-		    	selected = ui.item;
-		    }
-		    
-		    selected.each(function(i, el) { 
-		    	aItems.push($(el).data("aData"));
-		    });
-			
-			origTrs = aItems;
-			html = ui.helper.html();
-		};
-		
-		fnUpdate = function(event, ui) {
-			var prev = ui.item.prev();
-			
-			//can't add items outside of shows.
-			if (prev.hasClass("sb-footer")) {
-				alert("Cannot add an item outside a show.");
-				ui.item.remove();
-				return;
-			}
-			
-			aItemData = [];
-			oPrevData = prev.data("aData");
-			
-			//item was dragged in
-			if (origTrs !== undefined) {
-				
-				$("#show_builder_table tr.ui-draggable")
-					.empty()
-					.after(html);
-				
-				aItemData = origTrs;
-				origTrs = undefined;
-				fnAdd();
-			}
-			//item was reordered.
 			else {
-				aItemData.push(ui.item.data("aData"));
-				fnMove();
+				tr.addClass(cursorSelClass);
 			}
-		};
+			
+			//check if add button can still be enabled.
+			AIRTIME.library.events.enableAddButtonCheck();
+			
+			return false;
+		});
 		
-		return {
-			placeholder: "placeholder show-builder-placeholder ui-state-highlight",
-			forcePlaceholderSize: true,
-			items: 'tr:not(:first, :last, .sb-header, .sb-footer, .sb-not-allowed)',
-			receive: fnReceive,
-			update: fnUpdate
-		};
-	}());
+	};
 	
-	tableDiv.sortable(sortableConf);
+	mod.init = function(oTable) {
+		oSchedTable = oTable;
+	};
 	
-	$("#show_builder .fg-toolbar")
-		.append('<div class="ColVis TableTools"><button class="ui-button ui-state-default"><span>Delete</span></button></div>')
-		.click(fnRemoveSelectedItems);
+	return AIRTIME;
 	
-	//set things like a reference to the table.
-	AIRTIME.showbuilder.init(oTable);
-	
-	//add event to cursors.
-	tableDiv.find("tbody").on("click", "div.marker", function(event){
-		var tr = $(this).parents("tr");
-		
-		if (tr.hasClass("cursor-selected-row")) {
-			tr.removeClass("cursor-selected-row");
-		}
-		else {
-			tr.addClass("cursor-selected-row");
-		}
-		
-		return false;
-	});
-	
-});
+}(AIRTIME || {}));
\ No newline at end of file
diff --git a/airtime_mvc/public/js/airtime/showbuilder/main_builder.js b/airtime_mvc/public/js/airtime/showbuilder/main_builder.js
new file mode 100644
index 000000000..e1cc1f1c0
--- /dev/null
+++ b/airtime_mvc/public/js/airtime/showbuilder/main_builder.js
@@ -0,0 +1,128 @@
+$(document).ready(function(){
+	
+	var oBaseDatePickerSettings,
+		oBaseTimePickerSettings,
+		oRange;
+	
+	oBaseDatePickerSettings = {
+		dateFormat: 'yy-mm-dd',
+		onSelect: function(sDate, oDatePicker) {
+			var oDate,
+				dInput;
+			
+			dInput = $(this);			
+			oDate = dInput.datepicker( "setDate", sDate );
+		}
+	};
+	
+	oBaseTimePickerSettings = {
+		showPeriodLabels: false,
+		showCloseButton: true,
+		showLeadingZero: false,
+		defaultTime: '0:00'
+	};
+	
+	/*
+	 * Get the schedule range start in unix timestamp form (in seconds).
+	 * defaults to NOW if nothing is selected.
+	 * 
+	 * @param String sDatePickerId
+	 * 
+	 * @param String sTimePickerId
+	 * 
+	 * @return Number iTime
+	 */
+	function fnGetTimestamp(sDatePickerId, sTimePickerId) {
+		var date, 
+			time,
+			iTime,
+			iServerOffset,
+			iClientOffset;
+	
+		if ($(sDatePickerId).val() === "") {
+			return 0;
+		}
+		
+		date = $(sDatePickerId).val();
+		time = $(sTimePickerId).val();
+		
+		date = date.split("-");
+		time = time.split(":");
+		
+		//0 based month in js.
+		oDate = new Date(date[0], date[1]-1, date[2], time[0], time[1]);
+		
+		iTime = oDate.getTime(); //value is in millisec.
+		iTime = Math.round(iTime / 1000);
+		iServerOffset = serverTimezoneOffset;
+		iClientOffset = oDate.getTimezoneOffset() * -60;//function returns minutes
+		
+		//adjust for the fact the the Date object is in client time.
+		iTime = iTime + iClientOffset + iServerOffset;
+		
+		return iTime;
+	}
+	/*
+	 * Returns an object containing a unix timestamp in seconds for the start/end range
+	 * 
+	 * @return Object {"start", "end", "range"}
+	 */
+	function fnGetScheduleRange() {
+		var iStart, 
+			iEnd, 
+			iRange,
+			DEFAULT_RANGE = 60*60*24;
+		
+		iStart = fnGetTimestamp("#sb_date_start", "#sb_time_start");
+		iEnd = fnGetTimestamp("#sb_date_end", "#sb_time_end");
+		
+		iRange = iEnd - iStart;
+		
+		if (iRange === 0 || iEnd < iStart) {
+			iEnd = iStart + DEFAULT_RANGE;
+			iRange = DEFAULT_RANGE;
+		}
+		
+		return {
+			start: iStart,
+			end: iEnd,
+			range: iRange
+		};
+	}
+	
+	$("#sb_date_start").datepicker(oBaseDatePickerSettings);
+	$("#sb_time_start").timepicker(oBaseTimePickerSettings);
+	$("#sb_date_end").datepicker(oBaseDatePickerSettings);
+	$("#sb_time_end").timepicker(oBaseTimePickerSettings);
+	
+	$("#sb_submit").click(function(ev){
+		var fn,
+			oRange,
+			op,
+			oTable = $('#show_builder_table').dataTable();
+		
+		oRange = fnGetScheduleRange();
+		
+	    fn = oTable.fnSettings().fnServerData;
+	    fn.start = oRange.start;
+	    fn.end = oRange.end;
+	    
+	    op = $("div.sb-advanced-options");
+	    if (op.is(":visible")) {
+	    	
+	    	if (fn.ops === undefined) {
+	    		fn.ops = {};
+	    	}
+	    	fn.ops.showFilter = op.find("#sb_show_filter").val();
+	    	fn.ops.myShows = op.find("#sb_my_shows").is(":checked") ? 1 : 0;
+	    }
+		
+		oTable.fnDraw();
+	});
+	
+	oRange = fnGetScheduleRange();	
+	AIRTIME.showbuilder.fnServerData.start = oRange.start;
+	AIRTIME.showbuilder.fnServerData.end = oRange.end;
+		
+	AIRTIME.showbuilder.builderDataTable();
+});
\ No newline at end of file
diff --git a/dev_tools/auto_schedule_show.php b/dev_tools/auto_schedule_show.php
new file mode 100644
index 000000000..dee0aa7d1
--- /dev/null
+++ b/dev_tools/auto_schedule_show.php
@@ -0,0 +1,163 @@
+<?PHP
+
+/* 
+
+
+   The purpose of this script is to take a file from cc_files table, and insert it into
+   the schedule table. DB columns at the time of writing are
+ 
+ starts | ends | file_id | clip_length | fade_in | fade_out | cue_in | cue_out | media_item_played | instance_id
+ 
+ an example of data in this row is:
+ "9" | "2012-02-29 17:10:00" | "2012-02-29 17:15:05.037166" | 1 | "00:05:05.037166" | "00:00:00" | "00:00:00" | "00:00:00" | "00:05:05.037166" | FALSE | 5
+
+*/
+
+function query($conn, $query){
+    $result = pg_query($conn, $query);
+    if (!$result) {
+        echo "Error executing query $query.\n";
+        exit(1);
+    }
+    
+    return $result;
+}
+
+function getFileFromCcFiles($conn){
+    $query = "SELECT * from cc_files LIMIT 1";
+    
+    $result = query($conn, $query);
+    
+    $file = null;
+    while ($row = pg_fetch_array($result)) {
+        $file = $row;
+    }
+    
+    if (is_null($file)){
+        echo "Library is empty. Could not choose random file.";
+        exit(1);
+    }
+    
+    return $file;
+}
+
+function insertIntoCcShow($conn){
+    /* Step 1:
+     * Create a show
+     *  */
+
+    $query = "INSERT INTO cc_show (name, url, genre, description, color, background_color) VALUES ('test', '', '', '', '', '')";
+    echo $query.PHP_EOL;
+    $result = query($conn, $query);
+
+    $query = "SELECT currval('cc_show_id_seq');";
+    $result = pg_query($conn, $query);
+    if (!$result) {
+      echo "Error executing query $query.\n";
+      exit(1);
+    }
+
+    while ($row = pg_fetch_array($result)) {
+      $show_id = $row["currval"];
+    }
+    
+    return $show_id;
+}
+
+function insertIntoCcShowInstances($conn, $show_id, $starts, $ends, $file){
+    /* Step 2:
+     * Create a show instance.
+     * Column values:
+     * starts | ends | show_id | record | rebroadcast | instance_id | file_id | time_filled | last_scheduled | modified_instance
+     *  */
+     
+    $nowDateTime = new DateTime("now", new DateTimeZone("UTC"));
+
+    $now = $nowDateTime->format("Y-m-d H:i:s");
+
+
+    $columns = "(starts, ends, show_id, record, rebroadcast, instance_id, file_id, time_filled, last_scheduled, modified_instance)";
+    $values = "('$starts', '$ends', $show_id, 0, 0, NULL, NULL, '$file[length]', '$now', 'f')";
+    $query = "INSERT INTO cc_show_instances $columns values $values ";
+    echo $query.PHP_EOL;
+     
+    $result = query($conn, $query);
+
+    $query = "SELECT currval('cc_show_instances_id_seq');";
+    $result = pg_query($conn, $query);
+    if (!$result) {
+      echo "Error executing query $query.\n";
+      exit(1);
+    }
+
+    while ($row = pg_fetch_array($result)) {
+      $show_instance_id = $row["currval"];
+    }
+    
+    return $show_instance_id;
+}
+
+/*
+ * id | starts | ends | file_id | clip_length| fade_in | fade_out | cue_in | cue_out | media_item_played | instance_id
+ * 1 | 2012-02-29 23:25:00 | 2012-02-29 23:30:05.037166 | 1 | 00:05:05.037166 | 00:00:00 | 00:00:00 | 00:00:00 | 00:05:05.037166 | f | 5
+ */
+function insertIntoCcSchedule($conn, $file, $show_instance_id, $starts, $ends){
+    $columns = "(starts, ends, file_id, clip_length, fade_in, fade_out, cue_in, cue_out, media_item_played, instance_id)";
+    $values = "('$starts', '$ends', $file[id], '$file[length]', '00:00:00', '00:00:00', '00:00:00', '$file[length]', 'f', $show_instance_id)";
+    $query = "INSERT INTO cc_schedule $columns VALUES $values";
+    echo $query.PHP_EOL;
+    
+    $result = query($conn, $query);
+}
+
+function rabbitMqNotify(){
+    $ini_file = parse_ini_file("/etc/airtime/airtime.conf", true);
+    $url = "http://localhost/api/rabbitmq-do-push/format/json/api_key/".$ini_file["general"]["api_key"];
+
+    echo "Contacting $url".PHP_EOL;
+    $ch = curl_init($url);
+    curl_exec($ch);
+    curl_close($ch);    
+}
+
+$conn = pg_connect("host=localhost port=5432 dbname=airtime user=airtime password=airtime");
+if (!$conn) {
+    echo "Couldn't connect to Airtime DB.\n";
+    exit(1);
+}
+
+if (count($argv) > 1){
+    if ($argv[1] == "--clean"){    
+        $tables = array("cc_schedule", "cc_show_instances", "cc_show");
+        
+        foreach($tables as $table){
+            $query = "DELETE FROM $table";
+            echo $query.PHP_EOL;
+            query($conn, $query);
+        }
+        rabbitMqNotify();
+        exit(0);
+    } else { 
+        $str = <<<EOD
+This script schedules a file to play 30 seconds in the future. It 
+modifies the database tables cc_schedule, cc_show_instances and cc_show.
+You can clean up these tables using the --clean option.
+EOD;
+        echo $str.PHP_EOL;
+        exit(0);
+    }
+}
+
+$startDateTime = new DateTime("now + 30sec", new DateTimeZone("UTC"));
+$endDateTime = new DateTime("now + 1min 30sec", new DateTimeZone("UTC"));
+$starts = $startDateTime->format("Y-m-d H:i:s");
+$ends = $endDateTime->format("Y-m-d H:i:s");
+
+$file = getFileFromCcFiles($conn); 
+$show_id = insertIntoCcShow($conn);
+$show_instance_id = insertIntoCcShowInstances($conn, $show_id, $starts, $ends, $file);
+insertIntoCcSchedule($conn, $file, $show_instance_id, $starts, $ends);
+
+rabbitMqNotify();
+
+echo PHP_EOL."Show scheduled for $starts (UTC)".PHP_EOL;
diff --git a/dev_tools/toggle-pypo-debug.sh b/dev_tools/toggle-pypo-debug.sh
new file mode 100755
index 000000000..982c7435c
--- /dev/null
+++ b/dev_tools/toggle-pypo-debug.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+if [[ $EUID -ne 0 ]]; then
+   echo "This script must be run as root." 1>&2
+   exit 1
+fi
+
+usage () {
+    echo "Use --enable <user> or --disable flag. Enable is to set up environment" 
+    echo "for specified user. --disable is to reset it back to pypo user"
+}
+
+if [ "$1" = "--enable" ]; then
+
+    /etc/init.d/airtime-playout stop
+    /etc/init.d/airtime-playout start-liquidsoap
+
+    user=$2
+
+    echo "Changing ownership to user $1"
+    chown -Rv $user:$user /var/log/airtime/pypo
+    chown -v $user:$user /etc/airtime/pypo.cfg
+    chown -Rv $user:$user /var/tmp/airtime/pypo/
+    chmod -v a+r /etc/airtime/api_client.cfg
+elif [ "$1" = "--disable" ]; then
+
+    user="pypo"
+
+    echo "Changing ownership to user $1"
+    chown -Rv $user:$user /var/log/airtime/pypo
+    chown -v $user:$user /etc/airtime/pypo.cfg
+    chown -Rv $user:$user /var/tmp/airtime/pypo/
+    chmod -v a+r /etc/airtime/api_client.cfg    
+
+
+    /etc/init.d/airtime-playout stop-liquidsoap
+    /etc/init.d/airtime-playout start
+else
+    usage
+fi
diff --git a/python_apps/api_clients/api_client.py b/python_apps/api_clients/api_client.py
index c0b64ea01..1148dcdc1 100755
--- a/python_apps/api_clients/api_client.py
+++ b/python_apps/api_clients/api_client.py
@@ -81,14 +81,6 @@ class ApiClientInterface:
     def get_media(self, src, dst):
         pass
 
-    # Implementation: optional
-    #
-    # Called from: push loop
-    #
-    # Tell server that the scheduled *playlist* has started.
-    def notify_scheduled_item_start_playing(self, pkey, schedule):
-        pass
-
     # Implementation: optional
     # You dont actually have to implement this function for the liquidsoap playout to work.
     #
@@ -261,15 +253,15 @@ class AirTimeApiClient(ApiClientInterface):
         export_url = export_url.replace('%%api_key%%', self.config["api_key"])
 
         response = ""
-        status = 0
         try:
             response_json = self.get_response_from_server(export_url)
             response = json.loads(response_json)
-            status = response['check']
+            success = True
         except Exception, e:
             logger.error(e)
+            success = False
 
-        return status, response
+        return success, response
 
 
     def get_media(self, uri, dst):
@@ -285,32 +277,6 @@ class AirTimeApiClient(ApiClientInterface):
         except Exception, e:
             logger.error("%s", e)
 
-
-    """
-    Tell server that the scheduled *playlist* has started.
-    """
-    def notify_scheduled_item_start_playing(self, pkey, schedule):
-        logger = self.logger
-        playlist = schedule[pkey]
-        schedule_id = playlist["schedule_id"]
-        url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["update_item_url"])
-
-        url = url.replace("%%schedule_id%%", str(schedule_id))
-        logger.debug(url)
-        url = url.replace("%%api_key%%", self.config["api_key"])
-
-        try:
-            response = urllib.urlopen(url)
-            response = json.loads(response.read())
-            logger.info("API-Status %s", response['status'])
-            logger.info("API-Message %s", response['message'])
-
-        except Exception, e:
-            logger.error("Unable to connect - %s", e)
-
-        return response
-
-
     """
     This is a callback from liquidsoap, we use this to notify about the
     currently playing *song*.  We get passed a JSON string which we handed to
diff --git a/python_apps/pypo/logging.cfg b/python_apps/pypo/logging.cfg
index acff7007d..6dae7d9c5 100644
--- a/python_apps/pypo/logging.cfg
+++ b/python_apps/pypo/logging.cfg
@@ -1,8 +1,8 @@
 [loggers]
-keys=root,fetch,push,recorder
+keys=root,fetch,push,recorder,message_h
 
 [handlers]
-keys=pypo,recorder
+keys=pypo,recorder,message_h
 
 [formatters]
 keys=simpleFormatter
@@ -29,6 +29,12 @@ handlers=recorder
 qualname=recorder
 propagate=0
 
+[logger_message_h]
+level=DEBUG
+handlers=message_h
+qualname=message_h
+propagate=0
+
 [handler_pypo]
 class=logging.handlers.RotatingFileHandler
 level=DEBUG
@@ -41,6 +47,12 @@ level=DEBUG
 formatter=simpleFormatter
 args=("/var/log/airtime/pypo/show-recorder.log", 'a', 1000000, 5,)
 
+[handler_message_h]
+class=logging.handlers.RotatingFileHandler
+level=DEBUG
+formatter=simpleFormatter
+args=("/var/log/airtime/pypo/message-handler.log", 'a', 1000000, 5,)
+
 [formatter_simpleFormatter]
 format=%(asctime)s %(levelname)s - [%(filename)s : %(funcName)s() : line %(lineno)d] - %(message)s
 datefmt=
diff --git a/python_apps/pypo/pypo-cli.py b/python_apps/pypo/pypo-cli.py
index d82d0b782..300f57f9c 100644
--- a/python_apps/pypo/pypo-cli.py
+++ b/python_apps/pypo/pypo-cli.py
@@ -16,6 +16,7 @@ from Queue import Queue
 from pypopush import PypoPush
 from pypofetch import PypoFetch
 from recorder import Recorder
+from pypomessagehandler import PypoMessageHandler
 
 from configobj import ConfigObj
 
@@ -55,23 +56,16 @@ except Exception, e:
 class Global:
     def __init__(self):
         self.api_client = api_client.api_client_factory(config)
-        self.set_export_source('scheduler')
         
     def selfcheck(self):
         self.api_client = api_client.api_client_factory(config)
         return self.api_client.is_server_compatible()
-
-    def set_export_source(self, export_source):
-        self.export_source = export_source
-        self.cache_dir = config["cache_dir"] + self.export_source + '/'
-        self.schedule_file = self.cache_dir + 'schedule.pickle'
-        self.schedule_tracker_file = self.cache_dir + "schedule_tracker.pickle"
         
     def test_api(self):
         self.api_client.test()
 
 """
-    def check_schedule(self, export_source):
+    def check_schedule(self):
         logger = logging.getLogger()
 
         try:
@@ -127,11 +121,19 @@ if __name__ == '__main__':
     api_client = api_client.api_client_factory(config)
     api_client.register_component("pypo")
 
-    q = Queue()
-    
+    pypoFetch_q = Queue()
     recorder_q = Queue()
-
-    pp = PypoPush(q)
+    pypoPush_q = Queue()
+    
+    pmh = PypoMessageHandler(pypoFetch_q, recorder_q)
+    pmh.daemon = True
+    pmh.start()
+    
+    pf = PypoFetch(pypoFetch_q, pypoPush_q)
+    pf.daemon = True
+    pf.start()
+    
+    pp = PypoPush(pypoPush_q)
     pp.daemon = True
     pp.start()
 
@@ -139,12 +141,11 @@ if __name__ == '__main__':
     recorder.daemon = True
     recorder.start()
 
-    pf = PypoFetch(q, recorder_q)
-    pf.daemon = True
-    pf.start()
-
-    #pp.join()
+    pmh.join()
+    pp.join()
     pf.join()
+    recorder.join()
+    
     logger.info("pypo fetch exit")
     sys.exit()
 """
diff --git a/python_apps/pypo/pypo-notify.py b/python_apps/pypo/pypo-notify.py
index 42fb523fc..ef480691f 100644
--- a/python_apps/pypo/pypo-notify.py
+++ b/python_apps/pypo/pypo-notify.py
@@ -34,7 +34,7 @@ import json
 from configobj import ConfigObj
 
 # custom imports
-from util import *
+#from util import *
 from api_clients import *
 
 # Set up command-line options
diff --git a/python_apps/pypo/pypofetch.py b/python_apps/pypo/pypofetch.py
index 99c742d15..fbdf1ea2a 100644
--- a/python_apps/pypo/pypofetch.py
+++ b/python_apps/pypo/pypofetch.py
@@ -16,12 +16,6 @@ from datetime import datetime
 from datetime import timedelta
 import filecmp
 
-# For RabbitMQ
-from kombu.connection import BrokerConnection
-from kombu.messaging import Exchange, Queue, Consumer, Producer
-from kombu.exceptions import MessageStateError
-from kombu.simple import SimpleQueue
-
 from api_clients import api_client
 
 from configobj import ConfigObj
@@ -42,30 +36,31 @@ except Exception, e:
     sys.exit()
 
 class PypoFetch(Thread):
-    def __init__(self, q, recorder_q):
+    def __init__(self, pypoFetch_q, pypoPush_q):
         Thread.__init__(self)
         self.api_client = api_client.api_client_factory(config)
-        self.set_export_source('scheduler')
-        self.queue = q
-        self.recorder_queue = recorder_q
-        self.schedule_data = []
-        logger = logging.getLogger('fetch')
-        logger.info("PypoFetch: init complete")
+        self.fetch_queue = pypoFetch_q
+        self.push_queue = pypoPush_q
+        
+        self.logger = logging.getLogger();
+        
+        self.cache_dir = os.path.join(config["cache_dir"], "scheduler")
+        self.logger.debug("Cache dir %s", self.cache_dir)
 
-    def init_rabbit_mq(self):
-        logger = logging.getLogger('fetch')
-        logger.info("Initializing RabbitMQ stuff")
         try:
-            schedule_exchange = Exchange("airtime-pypo", "direct", durable=True, auto_delete=True)
-            schedule_queue = Queue("pypo-fetch", exchange=schedule_exchange, key="foo")
-            connection = BrokerConnection(config["rabbitmq_host"], config["rabbitmq_user"], config["rabbitmq_password"], config["rabbitmq_vhost"])
-            channel = connection.channel()
-            self.simple_queue = SimpleQueue(channel, schedule_queue)
+            if not os.path.isdir(dir):
+                """
+                We get here if path does not exist, or path does exist but
+                is a file. We are not handling the second case, but don't 
+                think we actually care about handling it.
+                """
+                self.logger.debug("Cache dir does not exist. Creating...")
+                os.makedirs(dir)
         except Exception, e:
-            logger.error(e)
-            return False
-            
-        return True
+            pass
+        
+        self.schedule_data = []
+        self.logger.info("PypoFetch: init complete")
     
     """
     Handle a message from RabbitMQ, put it into our yucky global var.
@@ -73,55 +68,51 @@ class PypoFetch(Thread):
     """
     def handle_message(self, message):
         try:        
-            logger = logging.getLogger('fetch')
-            logger.info("Received event from RabbitMQ: %s" % message)
+            self.logger.info("Received event from Pypo Message Handler: %s" % message)
             
             m =  json.loads(message)
             command = m['event_type']
-            logger.info("Handling command: " + command)
+            self.logger.info("Handling command: " + command)
         
             if command == 'update_schedule':
                 self.schedule_data  = m['schedule']
-                self.process_schedule(self.schedule_data, "scheduler", False)
+                self.process_schedule(self.schedule_data, False)
             elif command == 'update_stream_setting':
-                logger.info("Updating stream setting...")
+                self.logger.info("Updating stream setting...")
                 self.regenerateLiquidsoapConf(m['setting'])
             elif command == 'update_stream_format':
-                logger.info("Updating stream format...")
+                self.logger.info("Updating stream format...")
                 self.update_liquidsoap_stream_format(m['stream_format'])
             elif command == 'update_station_name':
-                logger.info("Updating station name...")
+                self.logger.info("Updating station name...")
                 self.update_liquidsoap_station_name(m['station_name'])
             elif command == 'cancel_current_show':
-                logger.info("Cancel current show command received...")
+                self.logger.info("Cancel current show command received...")
                 self.stop_current_show()
-            elif command == 'update_recorder_schedule':
-                temp = m
-                if temp is not None:
-                    self.parse_shows(temp)
-            elif command == 'cancel_recording':
-                self.recorder_queue.put('cancel_recording')
         except Exception, e:
-            logger.error("Exception in handling RabbitMQ message: %s", e)
+            import traceback
+            top = traceback.format_exc()
+            self.logger.error('Exception: %s', e)
+            self.logger.error("traceback: %s", top)
+            self.logger.error("Exception in handling Message Handler message: %s", e)
+
         
     def stop_current_show(self):
-        logger = logging.getLogger('fetch')
-        logger.debug('Notifying Liquidsoap to stop playback.')
+        self.logger.debug('Notifying Liquidsoap to stop playback.')
         try:
             tn = telnetlib.Telnet(LS_HOST, LS_PORT)
             tn.write('source.skip\n')
             tn.write('exit\n')
             tn.read_all()
         except Exception, e:
-            logger.debug(e)
-            logger.debug('Could not connect to liquidsoap')
+            self.logger.debug(e)
+            self.logger.debug('Could not connect to liquidsoap')
     
     def regenerateLiquidsoapConf(self, setting):
-        logger = logging.getLogger('fetch')
         existing = {}
         # create a temp file
         fh = open('/etc/airtime/liquidsoap.cfg', 'r')
-        logger.info("Reading existing config...")
+        self.logger.info("Reading existing config...")
         # read existing conf file and build dict
         while 1:
             line = fh.readline()
@@ -151,7 +142,7 @@ class PypoFetch(Thread):
         #restart flag
         restart = False
         
-        logger.info("Looking for changes...")
+        self.logger.info("Looking for changes...")
         # look for changes
         for s in setting:
             if "output_sound_device" in s[u'keyname'] or "icecast_vorbis_metadata" in s[u'keyname']:
@@ -159,13 +150,13 @@ class PypoFetch(Thread):
                 state_change_restart[stream] = False
                 # This is the case where restart is required no matter what
                 if (existing[s[u'keyname']] != s[u'value']):
-                    logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
+                    self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
                     restart = True;
             else:
                 stream, dump = s[u'keyname'].split('_',1)
                 if "_output" in s[u'keyname']:
                     if (existing[s[u'keyname']] != s[u'value']):
-                        logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
+                        self.logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
                         restart = True;
                         state_change_restart[stream] = True
                     elif ( s[u'value'] != 'disabled'):
@@ -177,22 +168,22 @@ class PypoFetch(Thread):
                     if stream not in change:
                         change[stream] = False
                     if not (s[u'value'] == existing[s[u'keyname']]):
-                        logger.info("Keyname: %s, Curent value: %s, New Value: %s", s[u'keyname'], existing[s[u'keyname']], s[u'value'])
+                        self.logger.info("Keyname: %s, Curent value: %s, New Value: %s", s[u'keyname'], existing[s[u'keyname']], s[u'value'])
                         change[stream] = True
                         
         # set flag change for sound_device alway True
-        logger.info("Change:%s, State_Change:%s...", change, state_change_restart)
+        self.logger.info("Change:%s, State_Change:%s...", change, state_change_restart)
         
         for k, v in state_change_restart.items():
             if k == "sound_device" and v:
                 restart = True
             elif v and change[k]:
-                logger.info("'Need-to-restart' state detected for %s...", k)
+                self.logger.info("'Need-to-restart' state detected for %s...", k)
                 restart = True
         # rewrite
         if restart:
             fh = open('/etc/airtime/liquidsoap.cfg', 'w')
-            logger.info("Rewriting liquidsoap.cfg...")
+            self.logger.info("Rewriting liquidsoap.cfg...")
             fh.write("################################################\n")
             fh.write("# THIS FILE IS AUTO GENERATED. DO NOT CHANGE!! #\n")
             fh.write("################################################\n")
@@ -214,17 +205,18 @@ class PypoFetch(Thread):
             fh.close()
             # restarting pypo.
             # we could just restart liquidsoap but it take more time somehow.
-            logger.info("Restarting pypo...")
+            self.logger.info("Restarting pypo...")
             sys.exit(0)
         else:
-            logger.info("No change detected in setting...")
+            self.logger.info("No change detected in setting...")
             self.update_liquidsoap_connection_status()
-    """
+
+    def update_liquidsoap_connection_status(self):
+        """
         updates the status of liquidsoap connection to the streaming server
         This fucntion updates the bootup time variable in liquidsoap script
     """
     def update_liquidsoap_connection_status(self):
-        logger = logging.getLogger('fetch')
         tn = telnetlib.Telnet(LS_HOST, LS_PORT)
         # update the boot up time of liquidsoap. Since liquidsoap is not restarting,
         # we are manually adjusting the bootup time variable so the status msg will get
@@ -242,7 +234,7 @@ class PypoFetch(Thread):
         # streamin info is in the form of:
         # eg. s1:true,2:true,3:false
         streams = stream_info.split(",")
-        logger.info(streams)
+        self.logger.info(streams)
         
         fake_time = current_time + 1
         for s in streams:
@@ -252,46 +244,35 @@ class PypoFetch(Thread):
             if(status == "true"):
                 self.api_client.notify_liquidsoap_status("OK", stream_id, str(fake_time))
                 
-        
-        
-    def set_export_source(self, export_source):
-        logger = logging.getLogger('fetch')
-        self.export_source = export_source
-        self.cache_dir = config["cache_dir"] + self.export_source + '/'
-        logger.info("Creating cache directory at %s", self.cache_dir)
-
-
     def update_liquidsoap_stream_format(self, stream_format):
         # Push stream metadata to liquidsoap
         # TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
         try:
-            logger = logging.getLogger('fetch')
-            logger.info(LS_HOST)
-            logger.info(LS_PORT)
+            self.logger.info(LS_HOST)
+            self.logger.info(LS_PORT)
             tn = telnetlib.Telnet(LS_HOST, LS_PORT)
             command = ('vars.stream_metadata_type %s\n' % stream_format).encode('utf-8')
-            logger.info(command)
+            self.logger.info(command)
             tn.write(command)
             tn.write('exit\n')
             tn.read_all()
         except Exception, e:
-            logger.error("Exception %s", e)
+            self.logger.error("Exception %s", e)
     
     def update_liquidsoap_station_name(self, station_name):
         # Push stream metadata to liquidsoap
         # TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
         try:
-            logger = logging.getLogger('fetch')
-            logger.info(LS_HOST)
-            logger.info(LS_PORT)
+            self.logger.info(LS_HOST)
+            self.logger.info(LS_PORT)
             tn = telnetlib.Telnet(LS_HOST, LS_PORT)
             command = ('vars.station_name %s\n' % station_name).encode('utf-8')
-            logger.info(command)
+            self.logger.info(command)
             tn.write(command)
             tn.write('exit\n')
             tn.read_all()
         except Exception, e:
-            logger.error("Exception %s", e)
+            self.logger.error("Exception %s", e)
 
     """
     Process the schedule
@@ -301,191 +282,157 @@ class PypoFetch(Thread):
        to the cache dir (Folder-structure: cache/YYYY-MM-DD-hh-mm-ss)
      - runs the cleanup routine, to get rid of unused cached files
     """
-    def process_schedule(self, schedule_data, export_source, bootstrapping):
-        logger = logging.getLogger('fetch')
-        playlists = schedule_data["playlists"]
+    def process_schedule(self, schedule_data, bootstrapping):      
+        self.logger.debug(schedule_data)
+        media = schedule_data["media"]
 
         # Download all the media and put playlists in liquidsoap "annotate" format
         try:
-             liquidsoap_playlists = self.prepare_playlists(playlists, bootstrapping)
-        except Exception, e: logger.error("%s", e)
+             media = self.prepare_media(media, bootstrapping)
+        except Exception, e: self.logger.error("%s", e)
 
         # Send the data to pypo-push
-        scheduled_data = dict()
-        scheduled_data['liquidsoap_playlists'] = liquidsoap_playlists
-        scheduled_data['schedule'] = playlists
-        self.queue.put(scheduled_data)
+        self.logger.debug("Pushing to pypo-push: "+ str(media))
+        self.push_queue.put(media)
 
+        """
+        TODO
         # cleanup
-        try: self.cleanup(self.export_source)
-        except Exception, e: logger.error("%s", e)
-    
-    def getDateTimeObj(self,time):
-        timeinfo = time.split(" ")
-        date = timeinfo[0].split("-")
-        time = timeinfo[1].split(":")
-    
-        date = map(int, date)
-        time = map(int, time)
-    
-        return datetime(date[0], date[1], date[2], time[0], time[1], time[2], 0, None)
+        try: self.cleanup()
+        except Exception, e: self.logger.error("%s", e)
+        """
 
-    def parse_shows(self, m):
-        logger = logging.getLogger('fetch')
-        logger.info("Parsing recording show schedules...")
-        shows_to_record = {}
-        shows = m['shows']
-        for show in shows:
-            show_starts = self.getDateTimeObj(show[u'starts'])
-            show_end = self.getDateTimeObj(show[u'ends'])
-            time_delta = show_end - show_starts
-
-            shows_to_record[show[u'starts']] = [time_delta, show[u'instance_id'], show[u'name'], m['server_timezone']]
-        self.recorder_queue.put(shows_to_record)
-        logger.info(shows_to_record)
-
-
-    """
-    In this function every audio file is cut as necessary (cue_in/cue_out != 0) 
-    and stored in a playlist folder.
-    file is e.g. 2010-06-23-15-00-00/17_cue_10.132-123.321.mp3
-    """
-    def prepare_playlists(self, playlists, bootstrapping):
-        logger = logging.getLogger('fetch')
-
-        liquidsoap_playlists = dict()
-
-        # Dont do anything if playlists is empty
-        if not playlists:
-            logger.debug("Schedule is empty.")
-            return liquidsoap_playlists
-
-        scheduleKeys = sorted(playlists.iterkeys())
+        
 
+    def prepare_media(self, media, bootstrapping):
+        """
+        Iterate through the list of media items in "media" and 
+        download them.
+        """
         try:
-            for pkey in scheduleKeys:
-                logger.info("Playlist starting at %s", pkey)
-                playlist = playlists[pkey]
+            mediaKeys = sorted(media.iterkeys())
+            for mkey in mediaKeys:
+                self.logger.debug("Media item starting at %s", mkey)
+                media_item = media[mkey]
+                
+                if bootstrapping:            
+                    self.check_for_previous_crash(media_item)
 
                 # create playlist directory
                 try:
-                    os.mkdir(self.cache_dir + str(pkey))
+                    """
+                    Extract year, month, date from mkey
+                    """
+                    y_m_d = mkey[0:10]
+                    download_dir = os.path.join(self.cache_dir, y_m_d)
+                    try:
+                        os.makedirs(os.path.join(self.cache_dir, y_m_d))
+                    except Exception, e:
+                        pass
+                    fileExt = os.path.splitext(media_item['uri'])[1]
+                    dst = os.path.join(download_dir, media_item['id']+fileExt)
                 except Exception, e:
-                    logger.warning(e)
+                    self.logger.warning(e)
+                
+                if self.handle_media_file(media_item, dst):
+                    entry = self.create_liquidsoap_annotation(media_item, dst)
+                    media_item['show_name'] = "TODO"
+                    media_item["annotation"] = entry
                 
-                ls_playlist = self.handle_media_file(playlist, pkey, bootstrapping)
-
-                liquidsoap_playlists[pkey] = ls_playlist
         except Exception, e:
-            logger.error("%s", e)
-        return liquidsoap_playlists
+            self.logger.error("%s", e)
+                
+        return media
+    
 
+    def create_liquidsoap_annotation(self, media, dst):
+        entry = \
+            'annotate:media_id="%s",liq_start_next="%s",liq_fade_in="%s",liq_fade_out="%s",liq_cue_in="%s",liq_cue_out="%s",schedule_table_id="%s":%s' \
+            % (media['id'], 0, \
+            float(media['fade_in']) / 1000, \
+            float(media['fade_out']) / 1000, \
+            float(media['cue_in']), \
+            float(media['cue_out']), \
+            media['row_id'], dst)
 
-    """
-    Download and cache the media files.
-    This handles both remote and local files.
-    Returns an updated ls_playlist string.
-    """
-    def handle_media_file(self, playlist, pkey, bootstrapping):
-        logger = logging.getLogger('fetch')
+        return entry
         
-        ls_playlist = []
+    def check_for_previous_crash(self, media_item):
+        start = media_item['start']
+        end = media_item['end']
         
         dtnow = datetime.utcnow()
         str_tnow_s = dtnow.strftime('%Y-%m-%d-%H-%M-%S')
-        
-        sortedKeys = sorted(playlist['medias'].iterkeys())
-        
-        for key in sortedKeys:
-            media = playlist['medias'][key]
-            logger.debug("Processing track %s", media['uri'])
+                        
+        if start <= str_tnow_s and str_tnow_s < end:
+            #song is currently playing and we just started pypo. Maybe there
+            #was a power outage? Let's restart playback of this song.
+            start_split = map(int, start.split('-'))
+            media_start = datetime(start_split[0], start_split[1], start_split[2], start_split[3], start_split[4], start_split[5], 0, None)
+            self.logger.debug("Found media item that started at %s.", media_start)
             
-            if bootstrapping:              
-                start = media['start']
-                end = media['end']
-                                
-                if end <= str_tnow_s:
-                    continue
-                elif start <= str_tnow_s and str_tnow_s < end:
-                    #song is currently playing and we just started pypo. Maybe there
-                    #was a power outage? Let's restart playback of this song.
-                    start_split = map(int, start.split('-'))
-                    media_start = datetime(start_split[0], start_split[1], start_split[2], start_split[3], start_split[4], start_split[5], 0, None)
-                    logger.debug("Found media item that started at %s.", media_start)
-                    
-                    delta = dtnow - media_start #we get a TimeDelta object from this operation
-                    logger.info("Starting media item  at %d second point", delta.seconds)
-                    media['cue_in'] = delta.seconds + 10
-                    td = timedelta(seconds=10)
-                    playlist['start'] = (dtnow + td).strftime('%Y-%m-%d-%H-%M-%S')
-                    logger.info("Crash detected, setting playlist to restart at %s", (dtnow + td).strftime('%Y-%m-%d-%H-%M-%S'))
+            delta = dtnow - media_start #we get a TimeDelta object from this operation
+            self.logger.info("Starting media item  at %d second point", delta.seconds)
             
+            """
+            Set the cue_in. This is used by Liquidsoap to determine at what point in the media
+            item it should start playing. If the cue_in happens to be > cue_out, then make cue_in = cue_out
+            """
+            media_item['cue_in'] = delta.seconds + 10 if delta.seconds + 10 < media_item['cue_out'] else media_item['cue_out']
+            
+            """
+            Set the start time, which is used by pypo-push to determine when a media item is scheduled.
+            Pushing the start time into the future will ensure pypo-push will push this to Liquidsoap.
+            """
+            td = timedelta(seconds=10)
+            media_item['start'] = (dtnow + td).strftime('%Y-%m-%d-%H-%M-%S')
+            self.logger.info("Crash detected, setting playlist to restart at %s", (dtnow + td).strftime('%Y-%m-%d-%H-%M-%S'))
+    
+    def handle_media_file(self, media_item, dst):
+        """
+        Download and cache the media item.
+        """
+        
+        self.logger.debug("Processing track %s", media_item['uri'])
 
-            fileExt = os.path.splitext(media['uri'])[1]
-            try:
-                dst = "%s%s/%s%s" % (self.cache_dir, pkey, media['id'], fileExt)
-
-                # download media file
-                self.handle_remote_file(media, dst)
-                
-                if True == os.access(dst, os.R_OK):
-                    # check filesize (avoid zero-byte files)
-                    try: fsize = os.path.getsize(dst)
-                    except Exception, e:
-                        logger.error("%s", e)
-                        fsize = 0
-
+        try:
+            #blocking function to download the media item
+            self.download_file(media_item, dst)
+            
+            if os.access(dst, os.R_OK):
+                # check filesize (avoid zero-byte files)
+                try: 
+                    fsize = os.path.getsize(dst)
                     if fsize > 0:
-                        pl_entry = \
-                        'annotate:export_source="%s",media_id="%s",liq_start_next="%s",liq_fade_in="%s",liq_fade_out="%s",liq_cue_in="%s",liq_cue_out="%s",schedule_table_id="%s":%s' \
-                        % (media['export_source'], media['id'], 0, \
-                            float(media['fade_in']) / 1000, \
-                            float(media['fade_out']) / 1000, \
-                            float(media['cue_in']), \
-                            float(media['cue_out']), \
-                            media['row_id'], dst)
+                        return True
+                except Exception, e:
+                    self.logger.error("%s", e)
+                    fsize = 0
+            else:
+                self.logger.warning("Cannot read file %s.", dst)
 
-                        """
-                        Tracks are only added to the playlist if they are accessible
-                        on the file system and larger than 0 bytes.
-                        So this can lead to playlists shorter than expectet.
-                        (there is a hardware silence detector for this cases...)
-                        """
-                        entry = dict()
-                        entry['type'] = 'file'
-                        entry['annotate'] = pl_entry
-                        entry['show_name'] = playlist['show_name']
-                        ls_playlist.append(entry)
-
-                    else:
-                        logger.warning("zero-size file - skipping %s. will not add it to playlist at %s", media['uri'], dst)
-
-                else:
-                    logger.warning("something went wrong. file %s not available. will not add it to playlist", dst)
-
-            except Exception, e: logger.info("%s", e)
-        return ls_playlist
+        except Exception, e: 
+            self.logger.info("%s", e)
+            
+        return False
 
 
     """
     Download a file from a remote server and store it in the cache.
     """
-    def handle_remote_file(self, media, dst):
-        logger = logging.getLogger('fetch')
+    def download_file(self, media_item, dst):
         if os.path.isfile(dst):
             pass
-            #logger.debug("file already in cache: %s", dst)
+            #self.logger.debug("file already in cache: %s", dst)
         else:
-            logger.debug("try to download %s", media['uri'])
-            self.api_client.get_media(media['uri'], dst)
+            self.logger.debug("try to download %s", media_item['uri'])
+            self.api_client.get_media(media_item['uri'], dst)
     
     """
     Cleans up folders in cache_dir. Look for modification date older than "now - CACHE_FOR"
     and deletes them.
     """
-    def cleanup(self, export_source):
-        logger = logging.getLogger('fetch')
-
+    def cleanup(self):
         offset = 3600 * int(config["cache_for"])
         now = time.time()
 
@@ -495,86 +442,42 @@ class PypoFetch(Thread):
                     timestamp = calendar.timegm(time.strptime(dir, "%Y-%m-%d-%H-%M-%S"))
                     if (now - timestamp) > offset:
                         try:
-                            logger.debug('trying to remove  %s - timestamp: %s', os.path.join(r, dir), timestamp)
+                            self.logger.debug('trying to remove  %s - timestamp: %s', os.path.join(r, dir), timestamp)
                             shutil.rmtree(os.path.join(r, dir))
                         except Exception, e:
-                            logger.error("%s", e)
+                            self.logger.error("%s", e)
                             pass
                         else:
-                            logger.info('sucessfully removed %s', os.path.join(r, dir))
+                            self.logger.info('sucessfully removed %s', os.path.join(r, dir))
                 except Exception, e:
-                    logger.error(e)
+                    self.logger.error(e)
 
 
     def main(self):
-        logger = logging.getLogger('fetch')
-
-        try: os.mkdir(self.cache_dir)
-        except Exception, e: pass
-
         # Bootstrap: since we are just starting up, we need to grab the
         # most recent schedule.  After that we can just wait for updates. 
-        status, self.schedule_data = self.api_client.get_schedule()
-        if status == 1:
-            logger.info("Bootstrap schedule received: %s", self.schedule_data)
-            self.process_schedule(self.schedule_data, "scheduler", True)
-        
-        # Bootstrap: since we are just starting up, we need to grab the
-        # most recent schedule.  After that we can just wait for updates.
-        try:
-            temp = self.api_client.get_shows_to_record()
-            if temp is not None:
-                self.parse_shows(temp)
-            logger.info("Bootstrap recorder schedule received: %s", temp)
-        except Exception, e:
-            logger.error(e)
-            
-        logger.info("Bootstrap complete: got initial copy of the schedule")
-
-
-        while not self.init_rabbit_mq():
-            logger.error("Error connecting to RabbitMQ Server. Trying again in few seconds")
-            time.sleep(5)
+        success, self.schedule_data = self.api_client.get_schedule()
+        if success:
+            self.logger.info("Bootstrap schedule received: %s", self.schedule_data)
+            self.process_schedule(self.schedule_data, True)
 
         loops = 1        
         while True:
-            logger.info("Loop #%s", loops)
+            self.logger.info("Loop #%s", loops)
             try:               
-                try:
-                    message = self.simple_queue.get(block=True)
-                    self.handle_message(message.payload)
-                    # ACK the message to take it off the queue
-                    message.ack()
-                except MessageStateError, m:
-                    logger.error("Message ACK error: %s", m)
+                message = self.fetch_queue.get(block=True, timeout=3600)
+                self.handle_message(message)
             except Exception, e:
-                """
-                There is a problem with the RabbitMq messenger service. Let's
-                log the error and get the schedule via HTTP polling
-                """
-                logger.error("Exception, %s", e)
+                self.logger.error("Exception, %s", e)
                 
-                status, self.schedule_data = self.api_client.get_schedule()
-                if status == 1:
-                    self.process_schedule(self.schedule_data, "scheduler", False)
-                """
-                Fetch recorder schedule
-                """
-                try:
-                    temp = self.api_client.get_shows_to_record()
-                    if temp is not None:
-                        self.parse_shows(temp)
-                    logger.info("updated recorder schedule received: %s", temp)
-                except Exception, e:
-                    logger.error(e)
+                success, self.schedule_data = self.api_client.get_schedule()
+                if success:
+                    self.process_schedule(self.schedule_data, False)
 
             loops += 1
 
-    """
-    Main loop of the thread:
-    Wait for schedule updates from RabbitMQ, but in case there arent any,
-    poll the server to get the upcoming schedule.
-    """
     def run(self):
-        while True:
-            self.main()
+        """
+        Entry point of the thread
+        """
+        self.main()
diff --git a/python_apps/pypo/pypomessagehandler.py b/python_apps/pypo/pypomessagehandler.py
new file mode 100644
index 000000000..75b0407ea
--- /dev/null
+++ b/python_apps/pypo/pypomessagehandler.py
@@ -0,0 +1,120 @@
+import logging
+import logging.config
+import sys
+from configobj import ConfigObj
+from threading import Thread
+import time
+# For RabbitMQ
+from kombu.connection import BrokerConnection
+from kombu.messaging import Exchange, Queue, Consumer, Producer
+from kombu.exceptions import MessageStateError
+from kombu.simple import SimpleQueue
+import json
+
+# configure logging
+logging.config.fileConfig("logging.cfg")
+
+# loading config file
+try:
+    config = ConfigObj('/etc/airtime/pypo.cfg')
+    LS_HOST = config['ls_host']
+    LS_PORT = config['ls_port']
+    POLL_INTERVAL = int(config['poll_interval'])
+
+except Exception, e:
+    logger = logging.getLogger('message_h')
+    logger.error('Error loading config file: %s', e)
+    sys.exit()
+
+class PypoMessageHandler(Thread):
+    def __init__(self, pq, rq):
+        Thread.__init__(self)
+        self.logger = logging.getLogger('message_h')
+        self.pypo_queue = pq
+        self.recorder_queue = rq
+        
+    def init_rabbit_mq(self):
+        self.logger.info("Initializing RabbitMQ stuff")
+        try:
+            schedule_exchange = Exchange("airtime-pypo", "direct", durable=True, auto_delete=True)
+            schedule_queue = Queue("pypo-fetch", exchange=schedule_exchange, key="foo")
+            connection = BrokerConnection(config["rabbitmq_host"], config["rabbitmq_user"], config["rabbitmq_password"], config["rabbitmq_vhost"])
+            channel = connection.channel()
+            self.simple_queue = SimpleQueue(channel, schedule_queue)
+        except Exception, e:
+            self.logger.error(e)
+            return False
+            
+        return True
+    
+    """
+    Handle a message from RabbitMQ, put it into our yucky global var.
+    Hopefully there is a better way to do this.
+    """
+    def handle_message(self, message):
+        try:        
+            self.logger.info("Received event from RabbitMQ: %s" % message)
+            
+            m =  json.loads(message)
+            command = m['event_type']
+            self.logger.info("Handling command: " + command)
+        
+            if command == 'update_schedule':
+                self.logger.info("Updating schdule...")
+                self.pypo_queue.put(message)
+            elif command == 'update_stream_setting':
+                self.logger.info("Updating stream setting...")
+                self.pypo_queue.put(message)
+            elif command == 'update_stream_format':
+                self.logger.info("Updating stream format...")
+                self.pypo_queue.put(message)
+            elif command == 'update_station_name':
+                self.logger.info("Updating station name...")
+                self.pypo_queue.put(message)
+            elif command == 'cancel_current_show':
+                self.logger.info("Cancel current show command received...")
+                self.pypo_queue.put(message)
+            elif command == 'update_recorder_schedule':
+                self.recorder_queue.put(message)
+            elif command == 'cancel_recording':
+                self.recorder_queue.put(message)
+        except Exception, e:
+            self.logger.error("Exception in handling RabbitMQ message: %s", e)
+
+    def main(self):
+        while not self.init_rabbit_mq():
+            self.logger.error("Error connecting to RabbitMQ Server. Trying again in few seconds")
+            time.sleep(5)
+
+        loops = 1
+        while True:
+            self.logger.info("Loop #%s", loops)
+            try:
+                message = self.simple_queue.get(block=True)
+                self.handle_message(message.payload)
+                # ACK the message to take it off the queue
+                message.ack()
+            except Exception, e:
+                """
+                sleep 5 seconds so that we don't spin inside this
+                while loop and eat all the CPU
+                """
+                time.sleep(5)
+                
+                """
+                There is a problem with the RabbitMq messenger service. Let's
+                log the error and get the schedule via HTTP polling
+                """
+                self.logger.error("Exception, %s", e)
+
+            loops += 1
+
+    """
+    Main loop of the thread:
+    Wait for schedule updates from RabbitMQ, but in case there arent any,
+    poll the server to get the upcoming schedule.
+    """
+    def run(self):
+        while True:
+            self.main()
+
diff --git a/python_apps/pypo/pypopush.py b/python_apps/pypo/pypopush.py
index 24f48c7cb..db153cd36 100644
--- a/python_apps/pypo/pypopush.py
+++ b/python_apps/pypo/pypopush.py
@@ -34,166 +34,147 @@ class PypoPush(Thread):
     def __init__(self, q):
         Thread.__init__(self)
         self.api_client = api_client.api_client_factory(config)
-        self.set_export_source('scheduler')
         self.queue = q
 
-        self.schedule = dict()
-        self.playlists = dict()
+        self.media = dict()
 
         self.liquidsoap_state_play = True
         self.push_ahead = 10
-
-    def set_export_source(self, export_source):
-        self.export_source = export_source
-        self.cache_dir = config["cache_dir"] + self.export_source + '/'
-        self.schedule_tracker_file = self.cache_dir + "schedule_tracker.pickle"
+        self.last_end_time = 0
         
-    """
-    The Push Loop - the push loop periodically checks if there is a playlist 
-    that should be scheduled at the current time.
-    If yes, the current liquidsoap playlist gets replaced with the corresponding one,
-    then liquidsoap is asked (via telnet) to reload and immediately play it.
-    """
-    def push(self, export_source):
-        logger = logging.getLogger('push')
+        self.logger = logging.getLogger('push')
+        
+    def push(self):
+        """
+        The Push Loop - the push loop periodically checks if there is a playlist 
+        that should be scheduled at the current time.
+        If yes, the current liquidsoap playlist gets replaced with the corresponding one,
+        then liquidsoap is asked (via telnet) to reload and immediately play it.
+        """
 
         timenow = time.time()
         # get a new schedule from pypo-fetch
         if not self.queue.empty():
             # make sure we get the latest schedule
             while not self.queue.empty():
-                scheduled_data = self.queue.get()
-            logger.debug("Received data from pypo-fetch")
-            self.schedule = scheduled_data['schedule']
-            self.playlists = scheduled_data['liquidsoap_playlists']
-            
-            logger.debug('schedule %s' % json.dumps(self.schedule))
-            logger.debug('playlists %s' % json.dumps(self.playlists))
+                self.media = self.queue.get()
+            self.logger.debug("Received data from pypo-fetch")          
+            self.logger.debug('media %s' % json.dumps(self.media))
 
-        schedule = self.schedule
-        playlists = self.playlists
+        media = self.media
         
         currently_on_air = False
-        if schedule:
+        if media:
             tnow = time.gmtime(timenow)
             tcoming = time.gmtime(timenow + self.push_ahead)
             str_tnow_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tnow[0], tnow[1], tnow[2], tnow[3], tnow[4], tnow[5])
             str_tcoming_s = "%04d-%02d-%02d-%02d-%02d-%02d" % (tcoming[0], tcoming[1], tcoming[2], tcoming[3], tcoming[4], tcoming[5])
                         
-            for pkey in schedule:
-                plstart = schedule[pkey]['start'][0:19]
-         
-                if str_tnow_s <= plstart and plstart < str_tcoming_s:
-                    logger.debug('Preparing to push playlist scheduled at: %s', pkey)
-                    playlist = schedule[pkey]
-                                       
-                    
-                    # We have a match, replace the current playlist and
-                    # force liquidsoap to refresh.
-                    if (self.push_liquidsoap(pkey, schedule, playlists) == 1):
-                        logger.debug("Pushed to liquidsoap, updating 'played' status.")
+            for key in media:
+                media_item = media[key]
+                item_start = media_item['start'][0:19]
+                
+                if str_tnow_s <= item_start and item_start < str_tcoming_s:
+                    """
+                    If the media item starts in the next 30 seconds, push it to the queue.
+                    """
+                    self.logger.debug('Preparing to push media item scheduled at: %s', key)
+                              
+                    if self.push_to_liquidsoap(media_item):
+                        self.logger.debug("Pushed to liquidsoap, updating 'played' status.")
                         
                         currently_on_air = True
                         self.liquidsoap_state_play = True
-
-                        # Call API to update schedule states
-                        logger.debug("Doing callback to server to update 'played' status.")
-                        self.api_client.notify_scheduled_item_start_playing(pkey, schedule)
-
-                show_start = schedule[pkey]['show_start']
-                show_end = schedule[pkey]['show_end']
-                
-                if show_start <= str_tnow_s and str_tnow_s < show_end:
-                    currently_on_air = True
-            """
-            If currently_on_air = False but liquidsoap_state_play = True then it means that Liquidsoap may
-            still be playing audio even though the show has ended ('currently_on_air = False' means no show is scheduled)
-            See CC-3231.
-            This is a temporary solution for Airtime 2.0
-            """                
-            if not currently_on_air and self.liquidsoap_state_play:
-                logger.debug('Notifying Liquidsoap to stop playback.')
-                try:
-                    tn = telnetlib.Telnet(LS_HOST, LS_PORT)
-                    tn.write('source.skip\n')
-                    tn.write('exit\n')
-                    tn.read_all()
-                except Exception, e:
-                    logger.debug(e)
-                    logger.debug('Could not connect to liquidsoap')
-
-                self.liquidsoap_state_play = False
-
-
-    def push_liquidsoap(self, pkey, schedule, playlists):
-        logger = logging.getLogger('push')
-
+                        
+    def push_to_liquidsoap(self, media_item):
+        """
+        This function looks at the media item, and either pushes it to the Liquidsoap
+        queue immediately, or if the queue is empty - waits until the start time of the
+        media item before pushing it. 
+        """        
         try:
-            playlist = playlists[pkey]
-            plstart = schedule[pkey]['start'][0:19]
-
-            #strptime returns struct_time in local time
-            #mktime takes a time_struct and returns a floating point
-            #gmtime Convert a time expressed in seconds since the epoch to a struct_time in UTC
-            #mktime: expresses the time in local time, not UTC. It returns a floating point number, for compatibility with time().
-
-            epoch_start = calendar.timegm(time.strptime(plstart, '%Y-%m-%d-%H-%M-%S'))
-            
-            #Return the time as a floating point number expressed in seconds since the epoch, in UTC.
-            epoch_now = time.time()
-
-            logger.debug("Epoch start: %s" % epoch_start)
-            logger.debug("Epoch now: %s" % epoch_now)
-
-            sleep_time = epoch_start - epoch_now;
-
-            if sleep_time < 0:
-                sleep_time = 0
-
-            logger.debug('sleeping for %s s' % (sleep_time))
-            time.sleep(sleep_time)
-
-            tn = telnetlib.Telnet(LS_HOST, LS_PORT)
-
-            #skip the currently playing song if any.
-            logger.debug("source.skip\n")
-            tn.write("source.skip\n")
-
-            # Get any extra information for liquidsoap (which will be sent back to us)
-            liquidsoap_data = self.api_client.get_liquidsoap_data(pkey, schedule)
-
-            #Sending schedule table row id string.
-            logger.debug("vars.pypo_data %s\n"%(liquidsoap_data["schedule_id"]))
-            tn.write(("vars.pypo_data %s\n"%liquidsoap_data["schedule_id"]).encode('utf-8'))
-
-            logger.debug('Preparing to push playlist %s' % pkey)
-            for item in playlist:
-                annotate = item['annotate']
-                tn.write(str('queue.push %s\n' % annotate.encode('utf-8')))
-
-                show_name = item['show_name']
-                tn.write(str('vars.show_name %s\n' % show_name.encode('utf-8')))
-
-            tn.write("exit\n")
-            logger.debug(tn.read_all())
-
-            status = 1
+            if media_item["start"] == self.last_end_time:
+                """
+                this media item is attached to the end of the last
+                track, so let's push it now so that Liquidsoap can start playing
+                it immediately after (and prepare crossfades if need be).
+                """
+                telnet_to_liquidsoap(media_item)
+                self.last_end_time = media_item["end"]
+            else:
+                """
+                this media item does not start right after a current playing track.
+                We need to sleep, and then wake up when this track starts.
+                """
+                self.sleep_until_start(media_item)
+                
+                self.telnet_to_liquidsoap(media_item)
+                self.last_end_time = media_item["end"]
         except Exception, e:
-            logger.error('%s', e)
-            status = 0
-        return status
+            return False
+            
+        return True
 
+    def sleep_until_start(self, media_item):
+        """
+        The purpose of this function is to look at the difference between
+        "now" and when the media_item starts, and sleep for that period of time.
+        After waking from sleep, this function returns.
+        """
+        
+        mi_start = media_item['start'][0:19]
+        
+        #strptime returns struct_time in local time
+        epoch_start = calendar.timegm(time.strptime(mi_start, '%Y-%m-%d-%H-%M-%S'))
+        
+        #Return the time as a floating point number expressed in seconds since the epoch, in UTC.
+        epoch_now = time.time()
+        
+        self.logger.debug("Epoch start: %s" % epoch_start)
+        self.logger.debug("Epoch now: %s" % epoch_now)
+
+        sleep_time = epoch_start - epoch_now
+
+        if sleep_time < 0:
+            sleep_time = 0
+
+        self.logger.debug('sleeping for %s s' % (sleep_time))
+        time.sleep(sleep_time)
+
+    def telnet_to_liquidsoap(self, media_item):
+        """
+        telnets to liquidsoap and pushes the media_item to its queue. Push the
+        show name of every media_item as well, just to keep Liquidsoap up-to-date
+        about which show is playing.
+        """
+        
+        tn = telnetlib.Telnet(LS_HOST, LS_PORT)
+        
+        #tn.write(("vars.pypo_data %s\n"%liquidsoap_data["schedule_id"]).encode('utf-8'))
+        
+        annotation = media_item['annotation']
+        msg = 'queue.push %s\n' % annotation.encode('utf-8')
+        tn.write(msg)
+        self.logger.debug(msg)
+        
+        show_name = media_item['show_name']
+        msg = 'vars.show_name %s\n' % show_name.encode('utf-8')
+        tn.write(msg)
+        self.logger.debug(msg)
+        
+        tn.write("exit\n")
+        self.logger.debug(tn.read_all())
+                     
     def run(self):
         loops = 0
         heartbeat_period = math.floor(30/PUSH_INTERVAL)
-        logger = logging.getLogger('push')
         
         while True:
             if loops % heartbeat_period == 0:
-                logger.info("heartbeat")
+                self.logger.info("heartbeat")
                 loops = 0
-            try: self.push('scheduler')
+            try: self.push()
             except Exception, e:
-                logger.error('Pypo Push Exception: %s', e)
+                self.logger.error('Pypo Push Exception: %s', e)
             time.sleep(PUSH_INTERVAL)
             loops += 1
diff --git a/python_apps/pypo/recorder.py b/python_apps/pypo/recorder.py
index 6347c4f76..89a95b001 100644
--- a/python_apps/pypo/recorder.py
+++ b/python_apps/pypo/recorder.py
@@ -169,26 +169,42 @@ class Recorder(Thread):
     def __init__(self, q):
         Thread.__init__(self)
         self.logger = logging.getLogger('recorder')
-        self.api_client = api_client.api_client_factory(config)
+        self.api_client = api_client.api_client_factory(config, self.logger)
         self.api_client.register_component("show-recorder")
         self.sr = None
         self.shows_to_record = {}
         self.server_timezone = ''
         self.queue = q
         self.logger.info("RecorderFetch: init complete")
+        self.loops = 0
 
     def handle_message(self):
         if not self.queue.empty():
-            msg = self.queue.get()
-            self.logger.info("Receivied msg from Pypo Fetch: %s", msg)
-            if msg == 'cancel_recording':
+            message = self.queue.get()
+            msg =  json.loads(message)
+            command = msg["event_type"]
+            self.logger.info("Received msg from Pypo Message Handler: %s", msg)
+            if command == 'cancel_recording':
                 if self.sr is not None and self.sr.is_recording():
                     self.sr.cancel_recording()
             else:
-                self.shows_to_record = msg
+                self.process_recorder_schedule(msg)
+                self.loops = 0
         
         if self.shows_to_record:
             self.start_record()
+    
+    def process_recorder_schedule(self, m):
+        self.logger.info("Parsing recording show schedules...")
+        temp_shows_to_record = {}
+        shows = m['shows']
+        for show in shows:
+            show_starts = getDateTimeObj(show[u'starts'])
+            show_end = getDateTimeObj(show[u'ends'])
+            time_delta = show_end - show_starts
+
+            temp_shows_to_record[show[u'starts']] = [time_delta, show[u'instance_id'], show[u'name'], m['server_timezone']]
+        self.shows_to_record = temp_shows_to_record
 
     def get_time_till_next_show(self):
         if len(self.shows_to_record) != 0:
@@ -247,21 +263,43 @@ class Recorder(Thread):
     def run(self):
         try:
             self.logger.info("Started...")
-    
+            # Bootstrap: since we are just starting up, we need to grab the
+            # most recent schedule.  After that we can just wait for updates.
+            try:
+                temp = self.api_client.get_shows_to_record()
+                if temp is not None:
+                    self.process_recorder_schedule(temp)
+                self.logger.info("Bootstrap recorder schedule received: %s", temp)
+            except Exception, e:
+                self.logger.error(e)
+                
+            self.logger.info("Bootstrap complete: got initial copy of the schedule")
+            
             recording = False
             
-            loops = 0
+            self.loops = 0
             heartbeat_period = math.floor(30/PUSH_INTERVAL)
             
             while True:
-                if loops % heartbeat_period == 0:
+                if self.loops % heartbeat_period == 0:
                     self.logger.info("heartbeat")
-                    loops = 0
+                if self.loops * PUSH_INTERVAL > 3600:
+                    self.loops = 0
+                    """
+                    Fetch recorder schedule
+                    """
+                    try:
+                        temp = self.api_client.get_shows_to_record()
+                        if temp is not None:
+                            self.process_recorder_schedule(temp)
+                        self.logger.info("updated recorder schedule received: %s", temp)
+                    except Exception, e:
+                        self.logger.error(e)
                 try: self.handle_message()
                 except Exception, e:
                     self.logger.error('Pypo Recorder Exception: %s', e)
                 time.sleep(PUSH_INTERVAL)
-                loops += 1
+                self.loops += 1
         except Exception,e :
             import traceback
             top = traceback.format_exc()