From fae71ed051907b9ce4919777fb3757ac8d558fd4 Mon Sep 17 00:00:00 2001 From: naomiaro Date: Tue, 22 Mar 2011 12:08:09 -0400 Subject: [PATCH 1/8] CC-1985 : Automatic rebroadcast of recorded content scheduling the recorded file in any rebroadcast shows. --- application/models/Schedule.php | 59 +++---------------- application/models/Shows.php | 30 +++++++++- .../models/airtime/map/CcScheduleTableMap.php | 2 +- build/schema.xml | 2 +- build/sql/schema.sql | 2 +- 5 files changed, 39 insertions(+), 56 deletions(-) diff --git a/application/models/Schedule.php b/application/models/Schedule.php index 5fe66f2ca..647b8b95e 100644 --- a/application/models/Schedule.php +++ b/application/models/Schedule.php @@ -24,41 +24,6 @@ class ScheduleGroup { return $result != "0"; } - /** - * Convert a date to an ID by stripping out all characters - * and padding with zeros. - * - * @param string $p_dateStr - */ - public static function dateToId($p_dateStr) { - $p_dateStr = str_replace(":", "", $p_dateStr); - $p_dateStr = str_replace(" ", "", $p_dateStr); - $p_dateStr = str_replace(".", "", $p_dateStr); - $p_dateStr = str_replace("-", "", $p_dateStr); - $p_dateStr = substr($p_dateStr, 0, 17); - $p_dateStr = str_pad($p_dateStr, 17, "0"); - return $p_dateStr; - } - - /** - * Add the two times together, return the result. - * - * @param string $p_baseTime - * Specified as YYYY-MM-DD HH:MM:SS - * - * @param string $p_addTime - * Specified as HH:MM:SS.nnnnnn - * - * @return string - * The end time, to the nearest second. - */ - // protected function calculateEndTime($p_startTime, $p_trackTime) { - // $p_trackTime = substr($p_startTime, 0, ); - // $start = new DateTime(); - // $interval = new DateInterval() - // - // } - /** * Add a music clip or playlist to the schedule. * @@ -77,6 +42,7 @@ class ScheduleGroup { */ public function add($show_instance, $p_datetime, $p_audioFileId = null, $p_playlistId = null, $p_options = null) { global $CC_CONFIG, $CC_DBC; + if (!is_null($p_audioFileId)) { // Schedule a single audio track @@ -92,19 +58,16 @@ class ScheduleGroup { if (empty($length)) { return new PEAR_Error("Length is empty."); } - if (!Schedule::isScheduleEmptyInRange($p_datetime, $length)) { - return new PEAR_Error("Schedule conflict.", 555); - } - + // Insert into the table $this->groupId = $CC_DBC->GetOne("SELECT nextval('schedule_group_id_seq')"); - $id = $this->dateToId($p_datetime); + $sql = "INSERT INTO ".$CC_CONFIG["scheduleTable"] - ." (playlist_id, starts, ends, clip_length, group_id, file_id)" - ." VALUES (0, TIMESTAMP '$p_datetime', " + ." (instance_id, starts, ends, clip_length, group_id, file_id, cue_out)" + ." VALUES ($show_instance, TIMESTAMP '$p_datetime', " ." (TIMESTAMP '$p_datetime' + INTERVAL '$length')," ." '$length'," - ." {$this->groupId}, $p_audioFileId)"; + ." {$this->groupId}, $p_audioFileId, '$length')"; $result = $CC_DBC->query($sql); if (PEAR::isError($result)) { //var_dump($sql); @@ -112,7 +75,8 @@ class ScheduleGroup { } return $this->groupId; - } elseif (!is_null($p_playlistId)){ + } + elseif (!is_null($p_playlistId)){ // Schedule a whole playlist // Load existing playlist @@ -130,7 +94,6 @@ class ScheduleGroup { // Insert all items into the schedule $this->groupId = $CC_DBC->GetOne("SELECT nextval('schedule_group_id_seq')"); - $id = $this->dateToId($p_datetime); $itemStartTime = $p_datetime; $plItems = $playlist->getContents(); @@ -157,7 +120,7 @@ class ScheduleGroup { } } - public function addAfter($show_instance, $p_groupId, $p_audioFileId) { + public function addFileAfter($show_instance, $p_groupId, $p_audioFileId) { global $CC_CONFIG, $CC_DBC; // Get the end time for the given entry $sql = "SELECT MAX(ends) FROM ".$CC_CONFIG["scheduleTable"] @@ -176,10 +139,6 @@ class ScheduleGroup { return $this->add($show_instance, $startTime, null, $p_playlistId); } - public function update() { - - } - /** * Remove the group from the schedule. * Note: does not check if it is in the past, you can remove anything. diff --git a/application/models/Shows.php b/application/models/Shows.php index 57ba0fb45..6dc28dcf7 100644 --- a/application/models/Shows.php +++ b/application/models/Shows.php @@ -652,8 +652,8 @@ class ShowInstance { return $res; } - public function addPlaylistToShow($plId) { - + public function addPlaylistToShow($plId) + { $sched = new ScheduleGroup(); $lastGroupId = $this->getLastGroupId(); @@ -666,6 +666,20 @@ class ShowInstance { } } + public function addFileToShow($file_id) + { + $sched = new ScheduleGroup(); + $lastGroupId = $this->getLastGroupId(); + + if(is_null($lastGroupId)) { + + $groupId = $sched->add($this->_instanceId, $this->getShowStart(), $file_id); + } + else { + $groupId = $sched->addFileAfter($this->_instanceId, $lastGroupId, $file_id); + } + } + public function scheduleShow($plIds) { foreach($plIds as $plId) { @@ -712,7 +726,17 @@ class ShowInstance { $showInstance = CcShowInstancesQuery::create() ->findPK($this->_instanceId); $showInstance->setDbRecordedFile($file_id) - ->save(); + ->save(); + + $rebroadcasts = CcShowInstancesQuery::create() + ->filterByDbOriginalShow($this->_instanceId) + ->find(); + + foreach ($rebroadcasts as $rebroadcast) { + + $rebroad = new ShowInstance($rebroadcast->getDbId()); + $rebroad->addFileToShow($file_id); + } } public function getTimeScheduled() { diff --git a/application/models/airtime/map/CcScheduleTableMap.php b/application/models/airtime/map/CcScheduleTableMap.php index 3f2502f1c..92afaab0c 100644 --- a/application/models/airtime/map/CcScheduleTableMap.php +++ b/application/models/airtime/map/CcScheduleTableMap.php @@ -39,7 +39,7 @@ class CcScheduleTableMap extends TableMap { $this->setPrimaryKeyMethodInfo('cc_schedule_id_seq'); // columns $this->addPrimaryKey('ID', 'DbId', 'INTEGER', true, null, null); - $this->addColumn('PLAYLIST_ID', 'DbPlaylistId', 'INTEGER', true, null, null); + $this->addColumn('PLAYLIST_ID', 'DbPlaylistId', 'INTEGER', false, null, null); $this->addColumn('STARTS', 'DbStarts', 'TIMESTAMP', true, null, null); $this->addColumn('ENDS', 'DbEnds', 'TIMESTAMP', true, null, null); $this->addColumn('GROUP_ID', 'DbGroupId', 'INTEGER', false, null, null); diff --git a/build/schema.xml b/build/schema.xml index 36941da37..f00ad14fb 100644 --- a/build/schema.xml +++ b/build/schema.xml @@ -234,7 +234,7 @@ - + diff --git a/build/sql/schema.sql b/build/sql/schema.sql index ee2f9b109..71ff970ef 100644 --- a/build/sql/schema.sql +++ b/build/sql/schema.sql @@ -345,7 +345,7 @@ DROP TABLE "cc_schedule" CASCADE; CREATE TABLE "cc_schedule" ( "id" serial NOT NULL, - "playlist_id" INTEGER NOT NULL, + "playlist_id" INTEGER, "starts" TIMESTAMP NOT NULL, "ends" TIMESTAMP NOT NULL, "group_id" INTEGER, From 582c05a192a0ea8680f82ee01029a3f8ab8a5a43 Mon Sep 17 00:00:00 2001 From: martin Date: Tue, 22 Mar 2011 13:39:01 -0400 Subject: [PATCH 2/8] CC-2077: Show is marked as Recording, when it's not set to record -Fixed --- public/js/playlist/playlist.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/playlist/playlist.js b/public/js/playlist/playlist.js index 82f277d1c..98b227381 100644 --- a/public/js/playlist/playlist.js +++ b/public/js/playlist/playlist.js @@ -169,7 +169,7 @@ function updatePlaybar(){ $('#playlist').text(currentShow[0].name); var recElem = $('.recording-show'); - currentShow[0].record ? recElem.show(): recElem.hide(); + (currentShow[0].record == "1") ? recElem.show(): recElem.hide(); } $('#show-length').empty(); From e92be3c186ca586626f790c729bcdd4dae3dda4b Mon Sep 17 00:00:00 2001 From: martin Date: Tue, 22 Mar 2011 18:04:27 -0400 Subject: [PATCH 3/8] CC-1990: Widget to display schedule and "Now Playing" on any website -First working version, needs to be called using: --- application/controllers/ApiController.php | 10 + application/models/Schedule.php | 2 +- application/models/Shows.php | 4 +- plugins/jquery.showinfo.js | 213 ++++++++++++++-------- 4 files changed, 146 insertions(+), 83 deletions(-) diff --git a/application/controllers/ApiController.php b/application/controllers/ApiController.php index b4dd0d3a2..f7ff37eb1 100644 --- a/application/controllers/ApiController.php +++ b/application/controllers/ApiController.php @@ -109,6 +109,16 @@ class ApiController extends Zend_Controller_Action $this->_helper->viewRenderer->setNoRender(true); $result = Schedule::GetPlayOrderRange(0, 1); + + $date = new Application_Model_DateHelper; + $timeNow = $date->getDate(); + $result = array("env"=>APPLICATION_ENV, + "schedulerTime"=>gmdate("Y-m-d H:i:s"), + "currentShow"=>Show_DAL::GetCurrentShow($timeNow), + "nextShow"=>Show_DAL::GetNextShows($timeNow, 5), + "timezone"=> date("T"), + "timezoneOffset"=> date("Z")); + //echo json_encode($result); header("Content-type: text/javascript"); echo $_GET['callback'].'('.json_encode($result).')'; diff --git a/application/models/Schedule.php b/application/models/Schedule.php index 6113eae2e..7435bf407 100644 --- a/application/models/Schedule.php +++ b/application/models/Schedule.php @@ -436,7 +436,7 @@ class Schedule { "current"=>Schedule::GetScheduledItemData($timeNow, 0), "next"=>Schedule::GetScheduledItemData($timeNow, 1, $next, "48 hours"), "currentShow"=>Show_DAL::GetCurrentShow($timeNow), - "nextShow"=>Show_DAL::GetNextShow($timeNow), + "nextShow"=>Show_DAL::GetNextShows($timeNow, 1), "timezone"=> date("T"), "timezoneOffset"=> date("Z"), "apiKey"=>$CC_CONFIG['apiKey'][0]); diff --git a/application/models/Shows.php b/application/models/Shows.php index ea2cf5401..f0a7a20bc 100644 --- a/application/models/Shows.php +++ b/application/models/Shows.php @@ -889,7 +889,7 @@ class Show_DAL { return $rows; } - public static function GetNextShow($timeNow) + public static function GetNextShows($timeNow, $limit) { global $CC_CONFIG, $CC_DBC; @@ -899,7 +899,7 @@ class Show_DAL { ." AND si.starts >= TIMESTAMP '$timeNow'" ." AND si.starts < TIMESTAMP '$timeNow' + INTERVAL '48 hours'" ." ORDER BY si.starts" - ." LIMIT 1"; + ." LIMIT $limit"; $rows = $CC_DBC->GetAll($sql); return $rows; diff --git a/plugins/jquery.showinfo.js b/plugins/jquery.showinfo.js index da5be1af8..c31381a10 100644 --- a/plugins/jquery.showinfo.js +++ b/plugins/jquery.showinfo.js @@ -2,25 +2,64 @@ $.fn.airtimeShowSchedule = function(options) { var defaults = { - updatePeriod: 5, //seconds + updatePeriod: 20, //seconds + sourceDomain: "http://localhost/", //where to get show status from }; var options = $.extend(defaults, options); return this.each(function() { var obj = $(this); + var sd; - obj.append("

On air today

"); - obj.append( - "
"+ - "" + - "" + - "" + - ""+ - ""+ - ""+ - ""+ - ""+ - "
13:15 - 13:30Program name Listen
13:15 - 13:30Lorem ipsum dolor
"); + getServerData(); + + function updateWidget(){ + var currentShow = sd.getCurrentShow(); + var nextShows = sd.getNextShows(); + + var currentShowName = ""; + var nextShowName = "" + + if (currentShow.length > 0){ + currentShowName = currentShow[0].getName(); + } + + if (nextShows.length > 0){ + nextShowName = nextShows[0].getName(); + } + + tableString = ""; + tableString += "

On air today

"; + tableString += ""+ + ""; + + var shows=currentShow.concat(nextShows); + + obj.empty(); + for (var i=0; i" + + "" + + "" + + ""; + } + + tableString += "
"+shows[i].getRange()+""+shows[i].getName()+" Listen
"; + + obj.append(tableString); + } + + function processData(data){ + sd = new ScheduleData(data); + updateWidget(); + } + + function getServerData(){ + $.ajax({ url: options.sourceDomain + "api/live-info/", dataType:"jsonp", success:function(data){ + processData(data); + }, error:function(jqXHR, textStatus, errorThrown){}}); + setTimeout(getServerData, defaults.updatePeriod*1000); + } }); }; })(jQuery); @@ -42,24 +81,42 @@ getServerData(); function updateWidget(){ - var currentShow = sd.getCurrentShowName(); - var timeRemaining = sd.getCurrentShowTimeRemaining(); - var timeElapsed = sd.getCurrentShowTimeElapsed(); - var showStatus = sd.getCurrentShowStatus(); + var currentShow = sd.getCurrentShow(); + var nextShows = sd.getNextShows(); - var nextShow = sd.getNextShowName(); - var nextShowRange = sd.getNextShowRange(); + var showStatus = "Offline"; + var currentShowName = ""; + var timeElapsed = ""; + var timeRemaining = ""; + + var nextShowName = ""; + var nextShowRange = ""; + + if (currentShow.length > 0){ + showStatus = "On Air Now"; + currentShowName = currentShow[0].getName(); + + timeElapsed = sd.getShowTimeElapsed(currentShow[0]); + timeRemaining = sd.getShowTimeRemaining(currentShow[0]); + } + + if (nextShows.length > 0){ + nextShowName = nextShows[0].getName(); + nextShowRange = nextShows[0].getRange(); + } obj.empty(); obj.append("Listen WADR Live"); obj.append("

"+showStatus+" >>

"); obj.append(""); - //refresh the UI + //refresh the UI to update the elapsed/remaining time setTimeout(updateWidget, 1000); } @@ -78,12 +135,23 @@ }; })(jQuery); -/* The rest of this file is the ScheduleData class */ +/* ScheduleData class BEGIN */ function ScheduleData(data){ this.data = data; this.estimatedSchedulePosixTime; - this.schedulePosixTime = this.convertDateToPosixTime(data.schedulerTime); + this.currentShow = new Array(); + for (var i=0; i< data.currentShow.length; i++){ + this.currentShow[i] = new Show(data.currentShow[i]); + } + + this.nextShows = new Array(); + for (var i=0; i< data.nextShow.length; i++){ + this.nextShows[i] = new Show(data.nextShow[i]); + } + + + this.schedulePosixTime = convertDateToPosixTime(data.schedulerTime); this.schedulePosixTime += parseInt(data.timezoneOffset)*1000; var date = new Date(); this.localRemoteTimeOffset = date.getTime() - this.schedulePosixTime; @@ -95,72 +163,57 @@ ScheduleData.prototype.secondsTimer = function(){ this.estimatedSchedulePosixTime = date.getTime() - this.localRemoteTimeOffset; } -ScheduleData.prototype.getCurrentShowName = function() { - var currentShow = this.data.currentShow; - if (currentShow.length > 0){ - return "Current: " + currentShow[0].name; - } else { - return ""; - } -}; +ScheduleData.prototype.getCurrentShow = function(){ + return this.currentShow; +} -ScheduleData.prototype.getCurrentShowStatus = function() { - var currentShow = this.data.currentShow; - if (currentShow.length > 0){ - return "On Air Now"; - } else { - return "Offline"; - } -}; +ScheduleData.prototype.getNextShows = function() { + return this.nextShows; +} -ScheduleData.prototype.getNextShowName = function() { - var nextShow = this.data.nextShow; - if (nextShow.length > 0){ - return "Next: " + nextShow[0].name; - } else { - return ""; - } -}; - -ScheduleData.prototype.getNextShowRange = function() { - var nextShow = this.data.nextShow; - if (nextShow.length > 0){ - return this.getTime(nextShow[0].start_timestamp) + " - " + this.getTime(nextShow[0].end_timestamp); - } else { - return ""; - } -}; - -ScheduleData.prototype.getCurrentShowTimeElapsed = function() { +ScheduleData.prototype.getShowTimeElapsed = function(show) { this.secondsTimer(); - var currentShow = this.data.currentShow; - if (currentShow.length > 0){ - var showStart = this.convertDateToPosixTime(currentShow[0].start_timestamp); - return this.convertToHHMMSS(this.estimatedSchedulePosixTime - showStart); - } else { - return ""; - } + + var showStart = convertDateToPosixTime(show.getStartTimestamp()); + return convertToHHMMSS(this.estimatedSchedulePosixTime - showStart); }; -ScheduleData.prototype.getCurrentShowTimeRemaining = function() { +ScheduleData.prototype.getShowTimeRemaining = function(show) { this.secondsTimer(); - var currentShow = this.data.currentShow; - if (currentShow.length > 0){ - var showEnd = this.convertDateToPosixTime(currentShow[0].end_timestamp); - return this.convertToHHMMSS(showEnd - this.estimatedSchedulePosixTime); - } else { - return ""; - } -}; -ScheduleData.prototype.getTime = function(timestamp) { - return timestamp.split(" ")[1]; + var showEnd = convertDateToPosixTime(show.getEndTimestamp()); + return convertToHHMMSS(showEnd - this.estimatedSchedulePosixTime); }; +/* ScheduleData class END */ +/* Show class BEGIN */ +function Show(showData){ + this.showData = showData; +} + +Show.prototype.getName = function(){ + return this.showData.name; +} +Show.prototype.getRange = function(){ + return getTime(this.showData.start_timestamp) + " - " + getTime(this.showData.end_timestamp); +} +Show.prototype.getStartTimestamp = function(){ + return this.showData.start_timestamp; +} +Show.prototype.getEndTimestamp = function(){ + return this.showData.end_timestamp; +} +/* Show class END */ + + +function getTime(timestamp) { + var time = timestamp.split(" ")[1].split(":"); + return time[0] + ":" + time[1]; +}; /* Takes an input parameter of milliseconds and converts these into * the format HH:MM:SS */ -ScheduleData.prototype.convertToHHMMSS = function(timeInMS){ +function convertToHHMMSS(timeInMS){ var time = parseInt(timeInMS); var hours = parseInt(time / 3600000); @@ -189,7 +242,7 @@ ScheduleData.prototype.convertToHHMMSS = function(timeInMS){ /* Takes in a string of format similar to 2011-02-07 02:59:57, * and converts this to epoch/posix time. */ -ScheduleData.prototype.convertDateToPosixTime = function(s){ +function convertDateToPosixTime(s){ var datetime = s.split(" "); var date = datetime[0].split("-"); From 8ffcd75682c89f723355160141a484608235c9af Mon Sep 17 00:00:00 2001 From: naomiaro Date: Tue, 22 Mar 2011 18:07:00 -0400 Subject: [PATCH 4/8] bug, forgot to delete useless line --- application/models/Schedule.php | 1 - 1 file changed, 1 deletion(-) diff --git a/application/models/Schedule.php b/application/models/Schedule.php index 6113eae2e..0034488d5 100644 --- a/application/models/Schedule.php +++ b/application/models/Schedule.php @@ -113,7 +113,6 @@ class ScheduleGroup { return $result; } $itemStartTime = $CC_DBC->getOne("SELECT TIMESTAMP '$itemStartTime' + INTERVAL '$trackLength'"); - $id = $this->dateToId($itemStartTime); } } RabbitMq::PushSchedule(); From f59225424665b0b6f912b3a0f3d099daf5bacefa Mon Sep 17 00:00:00 2001 From: naomiaro Date: Tue, 22 Mar 2011 18:20:22 -0400 Subject: [PATCH 5/8] CC-1630 : Automatic recording of shows scripts to install recorder using daemon tools. haven't tested without pulse audio yet. --- install/airtime-install.php | 3 + install/airtime-uninstall.php | 3 + pypo/install/pypo-install.py | 2 + python_apps/show-recorder/config.cfg | 2 +- .../install/recorder-daemontools-logger.sh | 2 + .../install/recorder-daemontools.sh | 14 ++ .../show-recorder/install/recorder-install.py | 128 ++++++++++++++++++ .../show-recorder/install/recorder-start.py | 16 +++ .../show-recorder/install/recorder-stop.py | 25 ++++ .../install/recorder-uninstall.py | 46 +++++++ python_apps/show-recorder/testsoundcloud.py | 59 -------- 11 files changed, 240 insertions(+), 60 deletions(-) create mode 100644 python_apps/show-recorder/install/recorder-daemontools-logger.sh create mode 100644 python_apps/show-recorder/install/recorder-daemontools.sh create mode 100644 python_apps/show-recorder/install/recorder-install.py create mode 100644 python_apps/show-recorder/install/recorder-start.py create mode 100644 python_apps/show-recorder/install/recorder-stop.py create mode 100644 python_apps/show-recorder/install/recorder-uninstall.py delete mode 100644 python_apps/show-recorder/testsoundcloud.py diff --git a/install/airtime-install.php b/install/airtime-install.php index 55d5ba068..ffcdea662 100644 --- a/install/airtime-install.php +++ b/install/airtime-install.php @@ -55,6 +55,9 @@ AirtimeInstall::SetUpPythonEggs(); echo PHP_EOL."*** Pypo Installation ***".PHP_EOL; system("python ".__DIR__."/../pypo/install/pypo-install.py"); +echo PHP_EOL."*** Recorder Installation ***".PHP_EOL; +system("python ".__DIR__."/../python_apps/show-recorder/install/recorder-install.py"); + echo "******************************* Install Complete *******************************".PHP_EOL; diff --git a/install/airtime-uninstall.php b/install/airtime-uninstall.php index 181006efd..d175856d5 100644 --- a/install/airtime-uninstall.php +++ b/install/airtime-uninstall.php @@ -83,5 +83,8 @@ AirtimeInstall::DeleteFilesRecursive($CC_CONFIG['storageDir']); $command = "python ".__DIR__."/../pypo/install/pypo-uninstall.py"; system($command); + +$command = "python ".__DIR__."/../python_apps/show-recorder/install/recorder-uninstall.py"; +system($command); echo "****************************** Uninstall Complete ******************************".PHP_EOL; diff --git a/pypo/install/pypo-install.py b/pypo/install/pypo-install.py index 07cb43cd9..cff58175e 100644 --- a/pypo/install/pypo-install.py +++ b/pypo/install/pypo-install.py @@ -44,6 +44,8 @@ def create_user(username): print "User already exists." #add pypo to audio group os.system("adduser " + username + " audio 1>/dev/null 2>&1") + #add pypo to pulse-access group + os.system("adduser " + username + " pulse-access 1>/dev/null 2>&1") def copy_dir(src_dir, dest_dir): if (os.path.exists(dest_dir)) and (dest_dir != "/"): diff --git a/python_apps/show-recorder/config.cfg b/python_apps/show-recorder/config.cfg index f75ea204c..d1bb763ab 100644 --- a/python_apps/show-recorder/config.cfg +++ b/python_apps/show-recorder/config.cfg @@ -6,4 +6,4 @@ show_schedule_url = 'Recorder/get-show-schedule/format/json' upload_file_url = 'Plupload/upload-recorded/format/json' # base path to store recordered shows at -base_recorded_files = '/home/naomi/Music/' +base_recorded_files = '/home/pypo/Music/' diff --git a/python_apps/show-recorder/install/recorder-daemontools-logger.sh b/python_apps/show-recorder/install/recorder-daemontools-logger.sh new file mode 100644 index 000000000..9673575db --- /dev/null +++ b/python_apps/show-recorder/install/recorder-daemontools-logger.sh @@ -0,0 +1,2 @@ +#!/bin/sh +exec setuidgid pypo multilog t ./main diff --git a/python_apps/show-recorder/install/recorder-daemontools.sh b/python_apps/show-recorder/install/recorder-daemontools.sh new file mode 100644 index 000000000..c36996b40 --- /dev/null +++ b/python_apps/show-recorder/install/recorder-daemontools.sh @@ -0,0 +1,14 @@ +#!/bin/sh +recorder_user="pypo" +export HOME="/home/pypo/" +# Location of pypo_cli.py Python script +recorder_path="/opt/recorder/bin/" +recorder_script="testrecordscript.py" +echo "*** Daemontools: starting daemon" +cd ${recorder_path} +exec 2>&1 +# Note the -u when calling python! we need it to get unbuffered binary stdout and stderr +exec setuidgid ${recorder_user} \ + python -u ${recorder_path}${recorder_script} \ + -f +# EOF diff --git a/python_apps/show-recorder/install/recorder-install.py b/python_apps/show-recorder/install/recorder-install.py new file mode 100644 index 000000000..1b8186391 --- /dev/null +++ b/python_apps/show-recorder/install/recorder-install.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import time +import os +import traceback +from optparse import * +import sys +import time +import datetime +import logging +import logging.config +import shutil +import string +import platform +from subprocess import Popen, PIPE, STDOUT + +if os.geteuid() != 0: + print "Please run this as root." + sys.exit(1) + +BASE_PATH = '/opt/recorder/' + +def create_path(path): + if not (os.path.exists(path)): + print "Creating directory " + path + os.makedirs(path) + +def create_user(username): + print "Checking for user "+username + p = Popen('id '+username, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) + output = p.stdout.read() + if (output[0:3] != "uid"): + # Make the pypo user + print "Creating user "+username + os.system("adduser --system --quiet --group --shell /bin/bash "+username) + + #set pypo password + p = os.popen('/usr/bin/passwd pypo 1>/dev/null 2>&1', 'w') + p.write('pypo\n') + p.write('pypo\n') + p.close() + else: + print "User already exists." + #add pypo to audio group + os.system("adduser " + username + " audio 1>/dev/null 2>&1") + +def copy_dir(src_dir, dest_dir): + if (os.path.exists(dest_dir)) and (dest_dir != "/"): + print "Removing old directory "+dest_dir + shutil.rmtree(dest_dir) + if not (os.path.exists(dest_dir)): + print "Copying directory "+src_dir+" to "+dest_dir + shutil.copytree(src_dir, dest_dir) + +def get_current_script_dir(): + current_script_dir = os.path.realpath(__file__) + index = current_script_dir.rindex('/') + print current_script_dir[0:index] + return current_script_dir[0:index] + + +try: + current_script_dir = get_current_script_dir() + print "Checking and removing any existing recorder processes" + os.system("python %s/recorder-uninstall.py 1>/dev/null 2>&1"% current_script_dir) + time.sleep(5) + + # Create users + create_user("pypo") + + print "Creating home directory" + create_path("/home/pypo") + os.system("chmod -R 755 /home/pypo") + os.system("chown -R pypo:pypo /home/pypo") + + print "Creating home directory" + create_path("/home/pypo/Music") + os.system("chmod -R 755 /home/pypo/Music") + os.system("chown -R pypo:pypo /home/pypo/Music") + + print "Creating log directories" + create_path("/var/log/recorder") + os.system("chmod -R 755 /var/log/recorder") + os.system("chown -R pypo:pypo /var/log/recorder") + + create_path(BASE_PATH) + create_path(BASE_PATH+"bin") + create_path(BASE_PATH+"cache") + create_path(BASE_PATH+"files") + create_path(BASE_PATH+"tmp") + create_path(BASE_PATH+"archive") + + copy_dir("%s/.."%current_script_dir, BASE_PATH+"bin/") + + print "Setting permissions" + os.system("chmod -R 755 "+BASE_PATH) + os.system("chown -R pypo:pypo "+BASE_PATH) + + print "Installing recorder daemon" + create_path("/etc/service/recorder") + create_path("/etc/service/recorder/log") + shutil.copy("%s/recorder-daemontools.sh"%current_script_dir, "/etc/service/recorder/run") + shutil.copy("%s/recorder-daemontools-logger.sh"%current_script_dir, "/etc/service/recorder/log/run") + os.system("chmod -R 755 /etc/service/recorder") + os.system("chown -R pypo:pypo /etc/service/recorder") + + print "Waiting for processes to start..." + time.sleep(5) + os.system("python %s/recorder-start.py" % (get_current_script_dir())) + time.sleep(2) + + found = True + + p = Popen('svstat /etc/service/recorder', shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True) + output = p.stdout.read() + if (output.find("unable to open supervise/ok: file does not exist") >= 0): + found = False + print output + + if not found: + print "Recorder install has completed, but daemontools is not running, please make sure you have it installed and then reboot." +except Exception, e: + print "exception:" + str(e) + sys.exit(1) + + + diff --git a/python_apps/show-recorder/install/recorder-start.py b/python_apps/show-recorder/install/recorder-start.py new file mode 100644 index 000000000..084b2b1ad --- /dev/null +++ b/python_apps/show-recorder/install/recorder-start.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import sys + +if os.geteuid() != 0: + print "Please run this as root." + sys.exit(1) + +try: + print "Starting daemontool script recorder" + os.system("svc -u /etc/service/recorder") + +except Exception, e: + print "exception:" + str(e) diff --git a/python_apps/show-recorder/install/recorder-stop.py b/python_apps/show-recorder/install/recorder-stop.py new file mode 100644 index 000000000..388c0bc4b --- /dev/null +++ b/python_apps/show-recorder/install/recorder-stop.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import sys + +if os.geteuid() != 0: + print "Please run this as root." + sys.exit(1) + +try: + print "Stopping daemontool script pypo" + os.system("svc -dx /etc/service/pypo 1>/dev/null 2>&1") + + if os.path.exists("/etc/service/pypo-fetch"): + os.system("svc -dx /etc/service/pypo-fetch 1>/dev/null 2>&1") + if os.path.exists("/etc/service/pypo-push"): + os.system("svc -dx /etc/service/pypo-push 1>/dev/null 2>&1") + + print "Stopping daemontool script pypo-liquidsoap" + os.system("svc -dx /etc/service/pypo-liquidsoap 1>/dev/null 2>&1") + os.system("killall liquidsoap") + +except Exception, e: + print "exception:" + str(e) diff --git a/python_apps/show-recorder/install/recorder-uninstall.py b/python_apps/show-recorder/install/recorder-uninstall.py new file mode 100644 index 000000000..f8ab96432 --- /dev/null +++ b/python_apps/show-recorder/install/recorder-uninstall.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import sys +import time + +if os.geteuid() != 0: + print "Please run this as root." + sys.exit(1) + +BASE_PATH = '/opt/recorder/' + +def remove_path(path): + os.system("rm -rf " + path) + +def remove_user(username): + os.system("killall -u %s 1>/dev/null 2>&1" % username) + + #allow all process to be completely closed before we attempt to delete user + print "Waiting for processes to close..." + time.sleep(5) + + os.system("deluser --remove-home " + username + " 1>/dev/null 2>&1") + +def get_current_script_dir(): + current_script_dir = os.path.realpath(__file__) + index = current_script_dir.rindex('/') + return current_script_dir[0:index] + +try: + os.system("python %s/recorder-stop.py" % get_current_script_dir()) + + print "Removing log directories" + remove_path("/var/log/recorder") + + print "Removing recorder files" + remove_path(BASE_PATH) + + print "Removing daemontool script recorder" + remove_path("rm -rf /etc/service/recorder") + + remove_user("pypo") + print "Uninstall complete." +except Exception, e: + print "exception:" + str(e) diff --git a/python_apps/show-recorder/testsoundcloud.py b/python_apps/show-recorder/testsoundcloud.py deleted file mode 100644 index 0ba6fd20d..000000000 --- a/python_apps/show-recorder/testsoundcloud.py +++ /dev/null @@ -1,59 +0,0 @@ -import webbrowser -import scapi - -# the host to connect to. Normally, this -# would be api.soundcloud.com -API_HOST = "api.soundcloud.com" - -# This needs to be the consumer ID you got from -# http://soundcloud.com/settings/applications/new -CONSUMER = "2CLCxcSXYzx7QhhPVHN4A" -# This needs to be the consumer secret password you got from -# http://soundcloud.com/settings/applications/new -CONSUMER_SECRET = "pZ7beWmF06epXLHVUP1ufOg2oEnIt9XhE8l8xt0bBs" - -# first, we create an OAuthAuthenticator that only knows about consumer -# credentials. This is done so that we can get an request-token as -# first step. -oauth_authenticator = scapi.authentication.OAuthAuthenticator(CONSUMER, - CONSUMER_SECRET, - None, - None) - -# The connector works with the authenticator to create and sign the requests. It -# has some helper-methods that allow us to do the OAuth-dance. -connector = scapi.ApiConnector(host=API_HOST, authenticator=oauth_authenticator) - -# First step is to get a request-token, and to let the user authorize that -# via the browser. -token, secret = connector.fetch_request_token() -authorization_url = connector.get_request_token_authorization_url(token) -webbrowser.open(authorization_url) -oauth_verifier = raw_input("please enter verifier code as seen in the browser:") - -# Now we create a new authenticator with the temporary token & secret we got from -# the request-token. This will give us the access-token -oauth_authenticator = scapi.authentication.OAuthAuthenticator(CONSUMER, - CONSUMER_SECRET, - token, - secret) - -# we need a new connector with the new authenticator! -connector = scapi.ApiConnector(API_HOST, authenticator=oauth_authenticator) -token, secret = connector.fetch_access_token(oauth_verifier) - - -# now we are finally ready to go - with all four parameters OAuth requires, -# we can setup an authenticator that allows for actual API-calls. -oauth_authenticator = scapi.authentication.OAuthAuthenticator(CONSUMER, - CONSUMER_SECRET, - token, - secret) - -# we pass the connector to a Scope - a Scope is essentially a path in the REST-url-space. -# Without any path-component, it's the root from which we can then query into the -# resources. -root = scapi.Scope(scapi.ApiConnector(host=API_HOST, authenticator=oauth_authenticator)) - -# Hey, nice meeting you! Connected to SoundCloud using OAuth will allow you to access protected resources, like the current user's name. -print "Hello, %s" % root.me().username From 44d83051b13c682db1742fec7d41cad19dfce976 Mon Sep 17 00:00:00 2001 From: naomiaro Date: Tue, 22 Mar 2011 18:41:02 -0400 Subject: [PATCH 6/8] CC-1630 : Automatic recording of shows using alsahw since users will have pulseaudio removed. --- pypo/install/pypo-install.py | 2 +- python_apps/show-recorder/testrecordscript.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pypo/install/pypo-install.py b/pypo/install/pypo-install.py index cff58175e..b764dcec9 100644 --- a/pypo/install/pypo-install.py +++ b/pypo/install/pypo-install.py @@ -45,7 +45,7 @@ def create_user(username): #add pypo to audio group os.system("adduser " + username + " audio 1>/dev/null 2>&1") #add pypo to pulse-access group - os.system("adduser " + username + " pulse-access 1>/dev/null 2>&1") + #os.system("adduser " + username + " pulse-access 1>/dev/null 2>&1") def copy_dir(src_dir, dest_dir): if (os.path.exists(dest_dir)) and (dest_dir != "/"): diff --git a/python_apps/show-recorder/testrecordscript.py b/python_apps/show-recorder/testrecordscript.py index 4c659cdcb..808918123 100644 --- a/python_apps/show-recorder/testrecordscript.py +++ b/python_apps/show-recorder/testrecordscript.py @@ -39,7 +39,7 @@ class Recorder(Thread): filename = self.filename.replace(" ", "-") filepath = "%s%s.%s" % (config["base_recorded_files"], filename, self.filetype) - command = "ecasound -i alsa -o %s -t:%s" % (filepath, length) + command = "ecasound -i alsahw,0,0 -o %s -t:%s" % (filepath, length) call(command, shell=True) return filepath From 1f4c08b1a3fd12e5fb785702275de6c4ae74c410 Mon Sep 17 00:00:00 2001 From: naomiaro Date: Wed, 23 Mar 2011 00:33:46 -0400 Subject: [PATCH 7/8] CC-1630 : Automatic recording of shows need to run show recorder as root to have access to soundcard using daemontools --- python_apps/show-recorder/install/recorder-daemontools.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/python_apps/show-recorder/install/recorder-daemontools.sh b/python_apps/show-recorder/install/recorder-daemontools.sh index c36996b40..d9cec7146 100644 --- a/python_apps/show-recorder/install/recorder-daemontools.sh +++ b/python_apps/show-recorder/install/recorder-daemontools.sh @@ -8,7 +8,5 @@ echo "*** Daemontools: starting daemon" cd ${recorder_path} exec 2>&1 # Note the -u when calling python! we need it to get unbuffered binary stdout and stderr -exec setuidgid ${recorder_user} \ - python -u ${recorder_path}${recorder_script} \ - -f +exec sudo python -u ${recorder_path}${recorder_script} -f # EOF From 8ebd70a61cb8f4f2d1849606a13eb8b7b085af24 Mon Sep 17 00:00:00 2001 From: naomiaro Date: Wed, 23 Mar 2011 00:36:34 -0400 Subject: [PATCH 8/8] CC-1630 : Automatic recording of shows don't need to give specific device, just using default. --- python_apps/show-recorder/testrecordscript.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_apps/show-recorder/testrecordscript.py b/python_apps/show-recorder/testrecordscript.py index 808918123..4c659cdcb 100644 --- a/python_apps/show-recorder/testrecordscript.py +++ b/python_apps/show-recorder/testrecordscript.py @@ -39,7 +39,7 @@ class Recorder(Thread): filename = self.filename.replace(" ", "-") filepath = "%s%s.%s" % (config["base_recorded_files"], filename, self.filetype) - command = "ecasound -i alsahw,0,0 -o %s -t:%s" % (filepath, length) + command = "ecasound -i alsa -o %s -t:%s" % (filepath, length) call(command, shell=True) return filepath