Merge branch 'master' of dev.sourcefabric.org:airtime

Conflicts:
	application/models/Shows.php
This commit is contained in:
paul.baranowski 2011-03-22 10:00:37 -04:00
commit b650abcbb8
24 changed files with 446 additions and 88 deletions

2
.gitignore vendored
View file

@ -1,5 +1,5 @@
.* .*
*.pyc *.pyc
files/ /files
pypo/liquidsoap/liquidsoap pypo/liquidsoap/liquidsoap
build/build.properties build/build.properties

View file

@ -14,6 +14,7 @@ require_once (__DIR__."/configs/constants.php");
require_once (__DIR__."/configs/conf.php"); require_once (__DIR__."/configs/conf.php");
require_once 'DB.php'; require_once 'DB.php';
require_once 'Soundcloud.php';
require_once 'Playlist.php'; require_once 'Playlist.php';
require_once 'StoredFile.php'; require_once 'StoredFile.php';
require_once 'Schedule.php'; require_once 'Schedule.php';

View file

@ -53,6 +53,9 @@ $CC_CONFIG = array(
'apiKey' => $values['api_key'], 'apiKey' => $values['api_key'],
'apiPath' => '/api/', 'apiPath' => '/api/',
'soundcloud-client-id' => '2CLCxcSXYzx7QhhPVHN4A',
'soundcloud-client-secret' => 'pZ7beWmF06epXLHVUP1ufOg2oEnIt9XhE8l8xt0bBs',
"rootDir" => __DIR__."/../..", "rootDir" => __DIR__."/../..",
'pearPath' => dirname(__FILE__).'/../../library/pear', 'pearPath' => dirname(__FILE__).'/../../library/pear',
'zendPath' => dirname(__FILE__).'/../../library/Zend', 'zendPath' => dirname(__FILE__).'/../../library/Zend',

View file

@ -101,6 +101,19 @@ class ApiController extends Zend_Controller_Action
exit; exit;
} }
public function liveInfoAction(){
global $CC_CONFIG;
// disable the view and the layout
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$result = Schedule::GetPlayOrderRange(0, 1);
//echo json_encode($result);
header("Content-type: text/javascript");
echo $_GET['callback'].'('.json_encode($result).')';
}
public function scheduleAction() public function scheduleAction()
{ {
global $CC_CONFIG; global $CC_CONFIG;

View file

@ -167,8 +167,18 @@ class PluploadController extends Zend_Controller_Action
$upload_dir = ini_get("upload_tmp_dir"); $upload_dir = ini_get("upload_tmp_dir");
$file = $this->upload($upload_dir); $file = $this->upload($upload_dir);
//$file->getRealFilePath(); if(Application_Model_Preference::GetDoSoundCloudUpload())
die('{"jsonrpc" : "2.0", "id" : '.$file->getId().' }'); {
$soundcloud = new ATSoundcloud();
$soundcloud->uploadTrack($file->getRealFilePath(), $file->getName());
}
$show_instance = $this->_getParam('show_instance');
$show = new ShowInstance($show_instance);
$show->setRecordedFile($file->getId());
die('{"jsonrpc" : "2.0", "id" : '.$file->getId().'}');
} }
public function pluploadAction() public function pluploadAction()

View file

@ -17,7 +17,7 @@ class Application_Form_Preferences extends Zend_Form
'value' => Application_Model_Preference::GetValue("station_name") 'value' => Application_Model_Preference::GetValue("station_name")
)); ));
$defaultFade = Application_Model_Preference::GetValue("default_fade"); $defaultFade = Application_Model_Preference::GetDefaultFade();
if($defaultFade == ""){ if($defaultFade == ""){
$defaultFade = '00:00:00.000000'; $defaultFade = '00:00:00.000000';
} }
@ -46,7 +46,7 @@ class Application_Form_Preferences extends Zend_Form
$this->addElement('checkbox', 'UseSoundCloud', array( $this->addElement('checkbox', 'UseSoundCloud', array(
'label' => 'Automatically Upload Recorded Shows To SoundCloud', 'label' => 'Automatically Upload Recorded Shows To SoundCloud',
'required' => false, 'required' => false,
'value' => Application_Model_Preference::GetValue("soundcloud_upload") 'value' => Application_Model_Preference::GetDoSoundCloudUpload()
)); ));
//SoundCloud Username //SoundCloud Username
@ -55,16 +55,16 @@ class Application_Form_Preferences extends Zend_Form
'label' => 'SoundCloud Username:', 'label' => 'SoundCloud Username:',
'required' => false, 'required' => false,
'filters' => array('StringTrim'), 'filters' => array('StringTrim'),
'value' => Application_Model_Preference::GetValue("soundcloud_user") 'value' => Application_Model_Preference::GetSoundCloudUser()
)); ));
//SoundCloud Password //SoundCloud Password
$this->addElement('password', 'SoundCloudPassword', array( $this->addElement('text', 'SoundCloudPassword', array(
'class' => 'input_text', 'class' => 'input_text',
'label' => 'SoundCloud Password:', 'label' => 'SoundCloud Password:',
'required' => false, 'required' => false,
'filters' => array('StringTrim'), 'filters' => array('StringTrim'),
'value' => Application_Model_Preference::GetValue("soundcloud_pass") 'value' => Application_Model_Preference::GetSoundCloudPassword()
)); ));
$this->addElement('submit', 'submit', array( $this->addElement('submit', 'submit', array(

View file

@ -118,7 +118,7 @@ class Application_Model_Preference
Application_Model_Preference::SetValue("soundcloud_password", $password); Application_Model_Preference::SetValue("soundcloud_password", $password);
} }
public static function GetSoundCloudUserPassword() { public static function GetSoundCloudPassword() {
return Application_Model_Preference::GetValue("soundcloud_password"); return Application_Model_Preference::GetValue("soundcloud_password");
} }

View file

@ -738,6 +738,14 @@ class ShowInstance {
RabbitMq::PushSchedule(); RabbitMq::PushSchedule();
} }
public function setRecordedFile($file_id)
{
$showInstance = CcShowInstancesQuery::create()
->findPK($this->_instanceId);
$showInstance->setDbRecordedFile($file_id)
->save();
}
public function getTimeScheduled() public function getTimeScheduled()
{ {
$instance_id = $this->getShowInstanceId(); $instance_id = $this->getShowInstanceId();

View file

@ -1,31 +1,54 @@
<?php <?php
require_once 'Soundcloud.php'; require_once 'soundcloud-api/Services/Soundcloud.php';
class ATSoundcloud {
/* private $_soundcloud;
require_once 'Soundcloud.php'; public function __construct()
{
global $CC_CONFIG;
$this->_soundcloud = new Services_Soundcloud($CC_CONFIG['soundcloud-client-id'], $CC_CONFIG['soundcloud-client-secret']);
}
$soundcloud = new Services_Soundcloud('2CLCxcSXYzx7QhhPVHN4A', 'pZ7beWmF06epXLHVUP1ufOg2oEnIt9XhE8l8xt0bBs'); private function getToken()
{
$username = Application_Model_Preference::GetSoundCloudUser();
$password = Application_Model_Preference::GetSoundCloudPassword();
$token = $soundcloud->accessTokenResourceOwner('naomiaro@gmail.com', 'airtime17'); if($username === "" || $password === "")
{
return false;
}
$track_data = array( $token = $this->_soundcloud->accessTokenResourceOwner($username, $password);
'track[sharing]' => 'private',
'track[title]' => 'Test', return $token;
'track[asset_data]' => '@/home/naomi/Music/testoutput.mp3' }
);
public function uploadTrack($filepath, $filename)
{
if($this->getToken())
{
$track_data = array(
'track[sharing]' => 'private',
'track[title]' => $filename,
'track[asset_data]' => '@' . $filepath
);
try {
$response = json_decode(
$this->_soundcloud->post('tracks', $track_data),
true
);
}
catch (Services_Soundcloud_Invalid_Http_Response_Code_Exception $e) {
echo $e->getMessage();
echo var_dump($track_data);
}
}
}
try {
$response = json_decode(
$soundcloud->post('tracks', $track_data),
true
);
}
catch (Services_Soundcloud_Invalid_Http_Response_Code_Exception $e) {
show_error($e->getMessage());
} }
*/

View file

@ -10,6 +10,7 @@
<div class="text-row top"> <div class="text-row top">
<span class="spl_playlength"><?php echo $item["cliplength"] ?></span> <span class="spl_playlength"><?php echo $item["cliplength"] ?></span>
<span class="spl_cue ui-state-default"></span>
<span class="spl_title"><?php echo $item["CcFiles"]['track_title'] ?></span> <span class="spl_title"><?php echo $item["CcFiles"]['track_title'] ?></span>
</div> </div>
<div class="text-row"> <div class="text-row">

View file

@ -49,6 +49,9 @@ AirtimeInstall::ChangeDirOwnerToWebserver($CC_CONFIG["storageDir"]);
echo "* Importing Sample Audio Clips".PHP_EOL; echo "* Importing Sample Audio Clips".PHP_EOL;
system(__DIR__."/../utils/airtime-import --copy ../audio_samples/ > /dev/null"); system(__DIR__."/../utils/airtime-import --copy ../audio_samples/ > /dev/null");
echo "* Python eggs Setup".PHP_EOL;
AirtimeInstall::SetUpPythonEggs();
echo PHP_EOL."*** Pypo Installation ***".PHP_EOL; echo PHP_EOL."*** Pypo Installation ***".PHP_EOL;
system("python ".__DIR__."/../pypo/install/pypo-install.py"); system("python ".__DIR__."/../pypo/install/pypo-install.py");

View file

@ -190,10 +190,17 @@ class AirtimeInstall {
system($command); system($command);
} }
public static function SetUpPythonEggs()
{
//install poster streaming upload
$command = "sudo easy_install poster";
@exec($command);
}
public static function DeleteFilesRecursive($p_path) public static function DeleteFilesRecursive($p_path)
{ {
$command = "rm -rf $p_path"; $command = "rm -rf $p_path";
exec($command); exec($command);
} }
} }

214
plugins/jquery.showinfo.js Normal file
View file

@ -0,0 +1,214 @@
(function($){
$.fn.airtimeShowSchedule = function(options) {
var defaults = {
updatePeriod: 5, //seconds
};
var options = $.extend(defaults, options);
return this.each(function() {
var obj = $(this);
obj.append("<h3>On air today</h3>");
obj.append(
"<table width='100%' border='0' cellspacing='0' cellpadding='0' class='widget widget no-playing-list small'>"+
"<tbody><tr>" +
"<td class='time'>13:15 - 13:30</td>" +
"<td><a href='#'>Program name</a> <a href='#' class='listen'>Listen</a></td>" +
"</tr>"+
"<tr>"+
"<td class='time'>13:15 - 13:30</td>"+
"<td><a href='#'>Lorem ipsum dolor</a></td>"+
"</tr>"+
"</tbody></table>");
});
};
})(jQuery);
(function($){
$.fn.airtimeLiveInfo = function(options) {
var defaults = {
updatePeriod: 5, //seconds
sourceDomain: "http://localhost/", //where to get show status from
audioStreamSource: "" //where to get audio stream from
};
var options = $.extend(defaults, options);
return this.each(function() {
var obj = $(this);
var sd;
getServerData();
function updateWidget(){
var currentShow = sd.getCurrentShowName();
var timeRemaining = sd.getCurrentShowTimeRemaining();
var timeElapsed = sd.getCurrentShowTimeElapsed();
var showStatus = sd.getCurrentShowStatus();
var nextShow = sd.getNextShowName();
var nextShowRange = sd.getNextShowRange();
obj.empty();
obj.append("<a id='listenWadrLive'><span>Listen WADR Live</span></a>");
obj.append("<h4>"+showStatus+" &gt;&gt;</h4>");
obj.append("<ul class='widget no-playing-bar'>" +
"<li class='current'>"+currentShow+ "<span id='time-elapsed' class='time-elapsed'>"+timeElapsed+"</span>" +
"<span id='time-remaining' class='time-remaining'>"+timeRemaining+"</span></li>" +
"<li class='next'>"+nextShow+"<span>"+nextShowRange+"</span></li>" +
"</ul>");
//refresh the UI
setTimeout(updateWidget, 1000);
}
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);
/* The rest of this file is the ScheduleData class */
function ScheduleData(data){
this.data = data;
this.estimatedSchedulePosixTime;
this.schedulePosixTime = this.convertDateToPosixTime(data.schedulerTime);
this.schedulePosixTime += parseInt(data.timezoneOffset)*1000;
var date = new Date();
this.localRemoteTimeOffset = date.getTime() - this.schedulePosixTime;
}
ScheduleData.prototype.secondsTimer = function(){
var date = new Date();
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.getCurrentShowStatus = function() {
var currentShow = this.data.currentShow;
if (currentShow.length > 0){
return "On Air Now";
} else {
return "Offline";
}
};
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() {
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 "";
}
};
ScheduleData.prototype.getCurrentShowTimeRemaining = function() {
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];
};
/* Takes an input parameter of milliseconds and converts these into
* the format HH:MM:SS */
ScheduleData.prototype.convertToHHMMSS = function(timeInMS){
var time = parseInt(timeInMS);
var hours = parseInt(time / 3600000);
time -= 3600000*hours;
var minutes = parseInt(time / 60000);
time -= 60000*minutes;
var seconds = parseInt(time / 1000);
hours = hours.toString();
minutes = minutes.toString();
seconds = seconds.toString();
if (hours.length == 1)
hours = "0" + hours;
if (minutes.length == 1)
minutes = "0" + minutes;
if (seconds.length == 1)
seconds = "0" + seconds;
if (hours == "00")
return minutes + ":" + seconds;
else
return hours + ":" + minutes + ":" + seconds;
}
/* 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){
var datetime = s.split(" ");
var date = datetime[0].split("-");
var time = datetime[1].split(":");
var year = date[0];
var month = date[1];
var day = date[2];
var hour = time[0];
var minute = time[1];
var sec = 0;
var msec = 0;
if (time[2].indexOf(".") != -1){
var temp = time[2].split(".");
sec = temp[0];
msec = temp[1];
} else
sec = time[2];
return Date.UTC(year, month, day, hour, minute, sec, msec);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

@ -15,7 +15,11 @@
#spl_sortable > li, #spl_sortable > li,
#side_playlist > div, #side_playlist > div,
#spl_editor, #spl_editor,
.spl_artist { .spl_artist,
.spl_cue_in,
.spl_fade_in,
.spl_cue_out,
.spl_fade_out {
clear: left; clear: left;
} }
@ -35,8 +39,8 @@
#spl_sortable { #spl_sortable {
list-style: none; list-style: none;
padding:0; padding:0;
height: 400px; height: 300px;
overflow-y: scroll; overflow: auto;
width:100%; width:100%;
margin-top:0; margin-top:0;
} }
@ -52,6 +56,10 @@
border: none; border: none;
} }
#spl_name {
}
.ui-icon-closethick { .ui-icon-closethick {
margin-top: 7px; margin-top: 7px;
} }
@ -72,6 +80,15 @@
font-size:12px; font-size:12px;
} }
/*#spl_editor {
height: 50px;
}*/
#spl_editor > div > span {
/* display: inline-block;
width: 150px;*/
}
.ui-icon-closethick, .ui-icon-closethick,
.ui-icon-play, .ui-icon-play,
.spl_fade_control, .spl_fade_control,
@ -239,13 +256,12 @@
margin: 0; margin: 0;
} }
dd.edit-error { .edit-error {
color:#b80000; color:#b80000;
margin:0; margin:0;
padding-bottom:0; padding-bottom:0;
font-size:12px; font-size:12px;
display:none; display:none;
clear: left;
} }
/*.edit-error:last-child { /*.edit-error:last-child {
@ -276,3 +292,26 @@ dd.edit-error {
top: 3px; top: 3px;
z-index: 3; z-index: 3;
} }
#spl_sortable li .spl_cue {
background-color: transparent;
float:right;
font-size: 9px;
height: 15px;
right: 35px;
width: 33px;
margin-top:2px;
cursor:pointer;
}
#spl_sortable li .spl_cue.ui-state-default {
background: transparent url(images/cue_playlist.png) no-repeat 0 0;
border:none;
}
#spl_sortable li .spl_cue.ui-state-default:hover {
background: transparent url(images/cue_playlist.png) no-repeat 0 -15px;
border:none;
}
#spl_sortable li .spl_cue.ui-state-active, #spl_sortable li .spl_cue.ui-state-active:hover {
background: transparent url(images/cue_playlist.png) no-repeat 0 -30px;
border:none;
}

View file

@ -205,13 +205,15 @@ function openFadeEditor(event) {
function openCueEditor(event) { function openCueEditor(event) {
event.stopPropagation(); event.stopPropagation();
var pos, url, li; var pos, url, li, icon;
li = $(this).parent().parent().parent(); li = $(this).parent().parent().parent();
icon = $(this);
pos = li.attr("id").split("_").pop(); pos = li.attr("id").split("_").pop();
if(li.hasClass("ui-state-active")) { if(li.hasClass("ui-state-active")) {
li.removeClass("ui-state-active"); li.removeClass("ui-state-active");
icon.attr("class", "spl_cue ui-state-default");
$("#cues_"+pos) $("#cues_"+pos)
.empty() .empty()
@ -220,6 +222,7 @@ function openCueEditor(event) {
return; return;
} }
icon.attr("class", "spl_cue ui-state-default ui-state-active");
url = '/Playlist/set-cue'; url = '/Playlist/set-cue';
highlightActive(li); highlightActive(li);
@ -253,7 +256,8 @@ function setSPLContent(json) {
$("#spl_sortable .ui-icon-closethick").click(deleteSPLItem); $("#spl_sortable .ui-icon-closethick").click(deleteSPLItem);
$(".spl_fade_control").click(openFadeEditor); $(".spl_fade_control").click(openFadeEditor);
$(".spl_playlength").click(openCueEditor); //$(".spl_playlength").click(openCueEditor);
$(".spl_cue").click(openCueEditor);
return false; return false;
} }
@ -487,7 +491,8 @@ function setUpSPL() {
$("#spl_sortable .ui-icon-closethick").click(deleteSPLItem); $("#spl_sortable .ui-icon-closethick").click(deleteSPLItem);
$(".spl_fade_control").click(openFadeEditor); $(".spl_fade_control").click(openFadeEditor);
$(".spl_playlength").click(openCueEditor); //$(".spl_playlength").click(openCueEditor);
$(".spl_cue").click(openCueEditor);
$("#spl_sortable").droppable(); $("#spl_sortable").droppable();
$("#spl_sortable" ).bind( "drop", addSPLItem); $("#spl_sortable" ).bind( "drop", addSPLItem);

View file

@ -36,14 +36,14 @@ def create_user(username):
os.system("adduser --system --quiet --group --shell /bin/bash "+username) os.system("adduser --system --quiet --group --shell /bin/bash "+username)
#set pypo password #set pypo password
p = os.popen('/usr/bin/passwd pypo 2>&1 1>/dev/null', 'w') p = os.popen('/usr/bin/passwd pypo 1>/dev/null 2>&1', 'w')
p.write('pypo\n') p.write('pypo\n')
p.write('pypo\n') p.write('pypo\n')
p.close() p.close()
else: else:
print "User already exists." print "User already exists."
#add pypo to audio group #add pypo to audio group
os.system("adduser " + username + " audio 2>&1 1>/dev/null") os.system("adduser " + username + " audio 1>/dev/null 2>&1")
def copy_dir(src_dir, dest_dir): def copy_dir(src_dir, dest_dir):
if (os.path.exists(dest_dir)) and (dest_dir != "/"): if (os.path.exists(dest_dir)) and (dest_dir != "/"):
@ -63,7 +63,7 @@ def get_current_script_dir():
try: try:
current_script_dir = get_current_script_dir() current_script_dir = get_current_script_dir()
print "Checking and removing any existing pypo processes" print "Checking and removing any existing pypo processes"
os.system("python %s/pypo-uninstall.py 2>&1 1>/dev/null"% current_script_dir) os.system("python %s/pypo-uninstall.py 1>/dev/null 2>&1"% current_script_dir)
time.sleep(5) time.sleep(5)
# Create users # Create users
@ -79,14 +79,8 @@ try:
create_path(BASE_PATH+"cache") create_path(BASE_PATH+"cache")
create_path(BASE_PATH+"files") create_path(BASE_PATH+"files")
create_path(BASE_PATH+"tmp") create_path(BASE_PATH+"tmp")
create_path(BASE_PATH+"files/basic")
create_path(BASE_PATH+"files/fallback")
create_path(BASE_PATH+"files/jingles")
create_path(BASE_PATH+"archive") create_path(BASE_PATH+"archive")
print "Copying pypo files"
shutil.copy("%s/../scripts/silence.mp3"%current_script_dir, BASE_PATH+"files/basic")
if platform.architecture()[0] == '64bit': if platform.architecture()[0] == '64bit':
print "Installing 64-bit liquidsoap binary" print "Installing 64-bit liquidsoap binary"
shutil.copy("%s/../liquidsoap/liquidsoap64"%current_script_dir, "%s/../liquidsoap/liquidsoap"%current_script_dir) shutil.copy("%s/../liquidsoap/liquidsoap64"%current_script_dir, "%s/../liquidsoap/liquidsoap"%current_script_dir)

View file

@ -10,10 +10,15 @@ if os.geteuid() != 0:
try: try:
print "Stopping daemontool script pypo" print "Stopping daemontool script pypo"
os.system("svc -dx /etc/service/pypo 2>/dev/null") 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" print "Stopping daemontool script pypo-liquidsoap"
os.system("svc -dx /etc/service/pypo-liquidsoap 2>/dev/null") os.system("svc -dx /etc/service/pypo-liquidsoap 1>/dev/null 2>&1")
os.system("killall liquidsoap") os.system("killall liquidsoap")
except Exception, e: except Exception, e:

View file

@ -15,13 +15,13 @@ def remove_path(path):
os.system("rm -rf " + path) os.system("rm -rf " + path)
def remove_user(username): def remove_user(username):
os.system("killall -u %s 2>&1 1>/dev/null" % 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 #allow all process to be completely closed before we attempt to delete user
print "Waiting for processes to close..." print "Waiting for processes to close..."
time.sleep(5) time.sleep(5)
os.system("deluser --remove-home " + username + " 1>/dev/null") os.system("deluser --remove-home " + username + " 1>/dev/null 2>&1")
def get_current_script_dir(): def get_current_script_dir():
current_script_dir = os.path.realpath(__file__) current_script_dir = os.path.realpath(__file__)
@ -39,6 +39,12 @@ try:
print "Removing daemontool script pypo" print "Removing daemontool script pypo"
remove_path("rm -rf /etc/service/pypo") remove_path("rm -rf /etc/service/pypo")
if os.path.exists("/etc/service/pypo-fetch"):
remove_path("rm -rf /etc/service/pypo-fetch")
if os.path.exists("/etc/service/pypo-push"):
remove_path("rm -rf /etc/service/pypo-push")
print "Removing daemontool script pypo-liquidsoap" print "Removing daemontool script pypo-liquidsoap"
remove_path("rm -rf /etc/service/pypo-liquidsoap") remove_path("rm -rf /etc/service/pypo-liquidsoap")

View file

@ -24,7 +24,7 @@ server.register(namespace="vars", "show_name", fun (s) -> begin show_name := s s
server.register(namespace="vars", "station_name", fun (s) -> begin station_name := s s end) server.register(namespace="vars", "station_name", fun (s) -> begin station_name := s s end)
default = single(conservative=true, "/opt/pypo/files/basic/silence.mp3") default = amplify(0.00001, noise())
default = rewrite_metadata([("artist","Airtime"), ("title", "offline")],default) default = rewrite_metadata([("artist","Airtime"), ("title", "offline")],default)
s = fallback(track_sensitive=false, [queue, default]) s = fallback(track_sensitive=false, [queue, default])
@ -69,6 +69,9 @@ if output_icecast_mp3 then
end end
if output_icecast_vorbis then if output_icecast_vorbis then
#remove metadata from ogg source and merge tracks to fix bug
#with vlc and mplayer disconnecting at the end of every track
ogg_s = add(normalize=false, [amplify(0.00001, noise()),s])
out_vorbis = output.icecast(%vorbis, out_vorbis = output.icecast(%vorbis,
host = icecast_host, port = icecast_port, host = icecast_host, port = icecast_port,
password = icecast_pass, mount = mount_point_vorbis, password = icecast_pass, mount = mount_point_vorbis,
@ -78,5 +81,5 @@ if output_icecast_vorbis then
url = icecast_url, url = icecast_url,
description = icecast_description, description = icecast_description,
genre = icecast_genre, genre = icecast_genre,
s) ogg_s)
end end

Binary file not shown.

View file

@ -1,5 +1,5 @@
# Hostname # Hostname
base_url = 'http://campcaster.dev/' base_url = 'http://localhost/'
show_schedule_url = 'Recorder/get-show-schedule/format/json' show_schedule_url = 'Recorder/get-show-schedule/format/json'

View file

@ -13,6 +13,7 @@ from poster.streaminghttp import register_openers
import urllib2 import urllib2
from subprocess import call from subprocess import call
from threading import Thread
# loading config file # loading config file
try: try:
@ -23,18 +24,46 @@ except Exception, e:
shows_to_record = {} shows_to_record = {}
class Recorder(Thread):
def record_show(filelength, filename, filetype="mp3"): def __init__ (self, show_instance, filelength, filename, filetype):
Thread.__init__(self)
self.filelength = filelength
self.filename = filename
self.filetype = filetype
self.show_instance = show_instance
length = str(filelength)+".0" def record_show(self):
filename = filename.replace(" ", "-")
filepath = "%s%s.%s" % (config["base_recorded_files"], filename, filetype)
command = "ecasound -i alsa -o %s -t:%s" % (filepath, filelength) length = str(self.filelength)+".0"
filename = self.filename.replace(" ", "-")
filepath = "%s%s.%s" % (config["base_recorded_files"], filename, self.filetype)
call(command, shell=True) command = "ecasound -i alsa -o %s -t:%s" % (filepath, length)
call(command, shell=True)
return filepath return filepath
def upload_file(self, filepath):
filename = os.path.split(filepath)[1]
# Register the streaming http handlers with urllib2
register_openers()
# headers contains the necessary Content-Type and Content-Length
# datagen is a generator object that yields the encoded parameters
datagen, headers = multipart_encode({"file": open(filepath, "rb"), 'name': filename, 'show_instance': self.show_instance})
url = config["base_url"] + config["upload_file_url"]
req = urllib2.Request(url, datagen, headers)
response = urllib2.urlopen(req).read().strip()
print response
def run(self):
filepath = self.record_show()
self.upload_file(filepath)
def getDateTimeObj(time): def getDateTimeObj(time):
@ -46,30 +75,42 @@ def getDateTimeObj(time):
return datetime.datetime(int(date[0]), int(date[1]), int(date[2]), int(time[0]), int(time[1]), int(time[2])) return datetime.datetime(int(date[0]), int(date[1]), int(date[2]), int(time[0]), int(time[1]), int(time[2]))
def process_shows(shows): def process_shows(shows):
global shows_to_record
shows_to_record = {}
for show in shows: for show in shows:
show_starts = getDateTimeObj(show[u'starts']) show_starts = getDateTimeObj(show[u'starts'])
show_end = getDateTimeObj(show[u'ends']) show_end = getDateTimeObj(show[u'ends'])
time_delta = show_end - show_starts time_delta = show_end - show_starts
shows_to_record[show[u'starts']] = time_delta shows_to_record[show[u'starts']] = [time_delta, show[u'instance_id']]
def check_record(): def check_record():
tnow = datetime.datetime.now() tnow = datetime.datetime.now()
sorted_show_keys = sorted(shows_to_record.keys()) sorted_show_keys = sorted(shows_to_record.keys())
print sorted_show_keys
start_time = sorted_show_keys[0] start_time = sorted_show_keys[0]
next_show = getDateTimeObj(start_time) next_show = getDateTimeObj(start_time)
print next_show
print tnow
delta = next_show - tnow delta = next_show - tnow
print delta
if delta <= datetime.timedelta(seconds=60): if delta <= datetime.timedelta(seconds=60):
print "sleeping %s seconds until show" % (delta.seconds)
time.sleep(delta.seconds) time.sleep(delta.seconds)
show_length = shows_to_record[start_time] show_length = shows_to_record[start_time][0]
filepath = record_show(show_length.seconds, start_time) show_instance = shows_to_record[start_time][1]
upload_file(filepath) show = Recorder(show_instance, show_length.seconds, start_time, filetype="mp3")
show.start()
#remove show from shows to record.
del shows_to_record[start_time]
def get_shows(): def get_shows():
@ -77,39 +118,21 @@ def get_shows():
url = config["base_url"] + config["show_schedule_url"] url = config["base_url"] + config["show_schedule_url"]
response = urllib.urlopen(url) response = urllib.urlopen(url)
data = response.read() data = response.read()
print data
response_json = json.loads(data) response_json = json.loads(data)
shows = response_json[u'shows'] shows = response_json[u'shows']
print shows print shows
if len(shows): if len(shows):
process_shows(shows) process_shows(shows)
check_record() check_record()
def upload_file(filepath):
filename = os.path.split(filepath)[1]
# Register the streaming http handlers with urllib2
register_openers()
# headers contains the necessary Content-Type and Content-Length
# datagen is a generator object that yields the encoded parameters
datagen, headers = multipart_encode({"file": open(filepath, "rb"), 'name': filename})
url = config["base_url"] + config["upload_file_url"]
req = urllib2.Request(url, datagen, headers)
response = urllib2.urlopen(req).read().strip()
print response
if __name__ == '__main__': if __name__ == '__main__':
while True: while True:
get_shows() get_shows()
time.sleep(30) time.sleep(5)