Merge branch '2.2.x' of dev.sourcefabric.org:airtime into 2.2.x

This commit is contained in:
denise 2012-11-23 16:54:39 -05:00
commit ddb6289c87
9 changed files with 120 additions and 63 deletions

View file

@ -48,7 +48,7 @@ class Application_Common_DateHelper
/** /**
* Get the week start date of this week in the format * Get the week start date of this week in the format
* YYYY-MM-DD * YYYY-MM-DD
* *
* @return String - week start date * @return String - week start date
*/ */
function getWeekStartDate() function getWeekStartDate()
@ -231,7 +231,7 @@ class Application_Common_DateHelper
if (2 !== substr_count($p_time, ":")){ if (2 !== substr_count($p_time, ":")){
return FALSE; return FALSE;
} }
if (1 === substr_count($p_time, ".")){ if (1 === substr_count($p_time, ".")){
list($hhmmss, $ms) = explode(".", $p_time); list($hhmmss, $ms) = explode(".", $p_time);
} else { } else {
@ -275,17 +275,17 @@ class Application_Common_DateHelper
return $dateTime; return $dateTime;
} }
/* Convenience method to return a date formatted into a String rather than a /* Convenience method to return a date formatted into a String rather than a
* DateTime object. Note that if an empty string is provided for $p_dateString * DateTime object. Note that if an empty string is provided for $p_dateString
* then the current time is provided. * then the current time is provided.
* *
* @param $p_dateString * @param $p_dateString
* Date string in UTC timezone. * Date string in UTC timezone.
* @param $p_format * @param $p_format
* Format which the string should be returned in. * Format which the string should be returned in.
* *
* @return string * @return string
* Date String in localtime * Date String in localtime
* */ * */
public static function ConvertToLocalDateTimeString($p_dateString, $p_format="Y-m-d H:i:s"){ public static function ConvertToLocalDateTimeString($p_dateString, $p_format="Y-m-d H:i:s"){
if (is_null($p_dateString) || strlen($p_dateString) == 0) if (is_null($p_dateString) || strlen($p_dateString) == 0)
@ -302,7 +302,7 @@ class Application_Common_DateHelper
/* /*
* Example input: "00:02:32.746562". Output is a DateInterval object * Example input: "00:02:32.746562". Output is a DateInterval object
* representing that 2 minute, 32.746562 second interval. * representing that 2 minute, 32.746562 second interval.
* *
*/ */
public static function getDateIntervalFromString($p_interval){ public static function getDateIntervalFromString($p_interval){
list($hour_min_sec, $subsec) = explode(".", $p_interval); list($hour_min_sec, $subsec) = explode(".", $p_interval);
@ -335,7 +335,7 @@ class Application_Common_DateHelper
$retVal['errMsg'] = "The year '$year' must be within the range of 1753 - 9999"; $retVal['errMsg'] = "The year '$year' must be within the range of 1753 - 9999";
} else if (!checkdate($month, $day, $year)) { } else if (!checkdate($month, $day, $year)) {
$retVal['success'] = false; $retVal['success'] = false;
$retVal['errMsg'] = "'$year-$month-$day' is not a valid date"; $retVal['errMsg'] = "'$year-$month-$day' is not a valid date";
} else { } else {
// check time // check time
if (isset($timeInfo)) { if (isset($timeInfo)) {

View file

@ -926,31 +926,33 @@ class ApiController extends Zend_Controller_Action
$data_arr = json_decode($data); $data_arr = json_decode($data);
if (!is_null($media_id) && isset($data_arr->title) && strlen($data_arr->title) < 1024) { if (!is_null($media_id)) {
if (isset($data_arr->title) &&
strlen($data_arr->title) < 1024) {
$previous_metadata = CcWebstreamMetadataQuery::create() $previous_metadata = CcWebstreamMetadataQuery::create()
->orderByDbStartTime('desc') ->orderByDbStartTime('desc')
->filterByDbInstanceId($media_id) ->filterByDbInstanceId($media_id)
->findOne(); ->findOne();
$do_insert = true; $do_insert = true;
if ($previous_metadata) { if ($previous_metadata) {
if ($previous_metadata->getDbLiquidsoapData() == $data_arr->title) { if ($previous_metadata->getDbLiquidsoapData() == $data_arr->title) {
Logging::debug("Duplicate found: ".$data_arr->title); Logging::debug("Duplicate found: ".$data_arr->title);
$do_insert = false; $do_insert = false;
}
}
if ($do_insert) {
$webstream_metadata = new CcWebstreamMetadata();
$webstream_metadata->setDbInstanceId($media_id);
$webstream_metadata->setDbStartTime(new DateTime("now", new DateTimeZone("UTC")));
$webstream_metadata->setDbLiquidsoapData($data_arr->title);
$webstream_metadata->save();
} }
} }
if ($do_insert) {
$webstream_metadata = new CcWebstreamMetadata();
$webstream_metadata->setDbInstanceId($media_id);
$webstream_metadata->setDbStartTime(new DateTime("now", new DateTimeZone("UTC")));
$webstream_metadata->setDbLiquidsoapData($data_arr->title);
$webstream_metadata->save();
}
} else { } else {
throw new Error("Unexpected error. media_id $media_id has a null stream value in cc_schedule!"); throw new Exception("Null value of media_id");
} }
$this->view->response = $data; $this->view->response = $data;

View file

@ -833,26 +833,60 @@ SQL;
} }
} }
/* Check if two events are less than or equal to 1 second apart
*/
public static function areEventsLinked($event1, $event2) {
$dt1 = DateTime::createFromFormat("Y-m-d-H-i-s", $event1['start']);
$dt2 = DateTime::createFromFormat("Y-m-d-H-i-s", $event2['start']);
$seconds = $dt2->getTimestamp() - $dt1->getTimestamp();
return $seconds <= 1;
}
/** /**
* Purpose of this function is to iterate through the entire * Streams are a 4 stage process.
* schedule array that was just built and fix the data up a bit. For * 1) start buffering stream 5 seconds ahead of its start time
* example, if we have two consecutive webstreams, we don't need the * 2) at the start time tell liquidsoap to switch to this source
* first webstream to shutdown the output, when the second one will * 3) at the end time, tell liquidsoap to stop reading this stream
* just switch it back on. Preventing this behaviour stops hiccups * 4) at the end time, tell liquidsoap to switch away from input.http source.
* in output sound. *
* When we have two streams back-to-back, some of these steps are unnecessary
* for the second stream. Instead of sending commands 1,2,3,4,1,2,3,4 we should
* send 1,2,1,2,3,4 - We don't need to tell liquidsoap to stop reading (#3), because #1
* of the next stream implies this when we pass in a new url. We also don't need #4.
*
* There's a special case here is well. When the back-to-back streams are the same, we
* can collapse the instructions 1,2,(3,4,1,2),3,4 to 1,2,3,4. We basically cut out the
* middle part. This function handles this.
*/ */
private static function filterData(&$data) private static function foldData(&$data)
{ {
$previous_key = null; $previous_key = null;
$previous_val = null; $previous_val = null;
$previous_previous_key = null;
$previous_previous_val = null;
$previous_previous_previous_key = null;
$previous_previous_previous_val = null;
foreach ($data as $k => $v) { foreach ($data as $k => $v) {
if ($v["type"] == "stream_buffer_start"
&& !is_null($previous_val)
&& $previous_val["type"] == "stream_output_end") {
if ($v["type"] == "stream_output_start"
&& !is_null($previous_previous_val)
&& $previous_previous_val["type"] == "stream_output_end"
&& self::areEventsLinked($previous_previous_val, $v)) {
unset($data[$previous_previous_previous_key]);
unset($data[$previous_previous_key]);
unset($data[$previous_key]); unset($data[$previous_key]);
if ($previous_previous_val['uri'] == $v['uri']) {
unset($data[$k]);
}
} }
$previous_previous_previous_key = $previous_previous_key;
$previous_previous_previous_val = $previous_previous_val;
$previous_previous_key = $previous_key;
$previous_previous_val = $previous_val;
$previous_key = $k; $previous_key = $k;
$previous_val = $v; $previous_val = $v;
} }
@ -870,7 +904,7 @@ SQL;
self::createInputHarborKickTimes($data, $range_start, $range_end); self::createInputHarborKickTimes($data, $range_start, $range_end);
self::createScheduledEvents($data, $range_start, $range_end); self::createScheduledEvents($data, $range_start, $range_end);
self::filterData($data["media"]); self::foldData($data["media"]);
return $data; return $data;
} }

View file

@ -266,8 +266,12 @@ class Application_Model_ShowBuilder
$row["instance"] = intval($p_item["si_id"]); $row["instance"] = intval($p_item["si_id"]);
$row["starts"] = $schedStartDT->format("H:i:s"); $row["starts"] = $schedStartDT->format("H:i:s");
$row["ends"] = $schedEndDT->format("H:i:s"); $row["ends"] = $schedEndDT->format("H:i:s");
$formatter = new LengthFormatter($p_item['file_length']); $cue_out = Application_Common_DateHelper::calculateLengthInSeconds($p_item['cue_out']);
$cue_in = Application_Common_DateHelper::calculateLengthInSeconds($p_item['cue_in']);
$run_time = $cue_out-$cue_in;
$formatter = new LengthFormatter(Application_Common_DateHelper::ConvertMSToHHMMSSmm($run_time*1000));
$row['runtime'] = $formatter->format(); $row['runtime'] = $formatter->format();
$row["title"] = $p_item["file_track_title"]; $row["title"] = $p_item["file_track_title"];

View file

@ -34,7 +34,7 @@ class Application_Model_Webstream implements Application_Model_LibraryEditable
public function getCreatorId() public function getCreatorId()
{ {
return $this->Webstream->getCcSubjs()->getDbId(); return $this->webstream->getDbCreatorId();
} }
public function getLastModified($p_type) public function getLastModified($p_type)
@ -51,7 +51,7 @@ class Application_Model_Webstream implements Application_Model_LibraryEditable
$di = new DateInterval("PT{$hours}H{$min}M{$sec}S"); $di = new DateInterval("PT{$hours}H{$min}M{$sec}S");
return $di->format("%Hh %Im"); return $di->format("%Hh %Im");
} }
return ""; return "";
} }

View file

@ -153,12 +153,24 @@ function buildplaylist(p_url, p_playIndex) {
continue; continue;
} }
} else if (data[index]['type'] == 1) { } else if (data[index]['type'] == 1) {
media = {title: data[index]['element_title'], var mime = data[index]['mime'];
artist: data[index]['element_artist'], if (mime.search(/mp3/i) > 0 || mime.search(/mpeg/i) > 0) {
mp3:data[index]['uri'] key = "mp3";
}; } else if (mime.search(/og(g|a)/i) > 0 || mime.search(/vorbis/i) > 0) {
key = "oga";
} else if (mime.search(/mp4/i) > 0) {
key = "m4a";
} else if (mime.search(/wav/i) > 0) {
key = "wav";
}
if (key) {
media = {title: data[index]['element_title'],
artist: data[index]['element_artist']
};
media[key] = data[index]['uri']
}
} }
console.log(data[index]);
if (media && isAudioSupported(data[index]['mime'])) { if (media && isAudioSupported(data[index]['mime'])) {
// javascript doesn't support associative array with numeric key // javascript doesn't support associative array with numeric key
// so we need to remove the gap if we skip any of tracks due to // so we need to remove the gap if we skip any of tracks due to

View file

@ -15,5 +15,6 @@ function isAudioSupported(mime){
//is adding a javascript library to do the work for you, which seems like overkill.... //is adding a javascript library to do the work for you, which seems like overkill....
return (!!audio.canPlayType && audio.canPlayType(bMime) != "") || return (!!audio.canPlayType && audio.canPlayType(bMime) != "") ||
(mime.indexOf("mp3") != -1 && navigator.mimeTypes ["application/x-shockwave-flash"] != undefined) || (mime.indexOf("mp3") != -1 && navigator.mimeTypes ["application/x-shockwave-flash"] != undefined) ||
(mime.indexOf("mp4") != -1 && navigator.mimeTypes ["application/x-shockwave-flash"] != undefined); (mime.indexOf("mp4") != -1 && navigator.mimeTypes ["application/x-shockwave-flash"] != undefined) ||
(mime.indexOf("mpeg") != -1 && navigator.mimeTypes ["application/x-shockwave-flash"] != undefined);
} }

View file

@ -975,7 +975,6 @@ function addProgressIcon(id) {
} }
function checkLibrarySCUploadStatus(){ function checkLibrarySCUploadStatus(){
var url = '/Library/get-upload-to-soundcloud-status', var url = '/Library/get-upload-to-soundcloud-status',
span, span,
id; id;
@ -1028,15 +1027,22 @@ function addQtipToSCIcons(){
}); });
} }
else if($(this).hasClass("soundcloud")){ else if($(this).hasClass("soundcloud")){
var sc_id = $(this).parent().parent().data("aData").soundcloud_id;
$(this).qtip({ $(this).qtip({
content: { content: {
//text: "The soundcloud id for this file is: "+sc_id
text: "Retrieving data from the server...", text: "Retrieving data from the server...",
ajax: { ajax: {
url: "/Library/get-upload-to-soundcloud-status", url: "/Library/get-upload-to-soundcloud-status",
type: "post", type: "post",
data: ({format: "json", id : id, type: "file"}), data: ({format: "json", id : id, type: "file"}),
success: function(json, status){ success: function(json, status){
this.set('content.text', "The soundcloud id for this file is: "+json.sc_id); id = sc_id;
if (id == undefined) {
id = json.sc_id;
}
this.set('content.text', "The soundcloud id for this file is: "+id);
} }
} }
}, },

View file

@ -11,11 +11,7 @@ set("server.telnet.port", 1234)
webstream_enabled = ref false webstream_enabled = ref false
time = ref string_of(gettimeofday()) time = ref string_of(gettimeofday())
queue = audio_to_stereo(id="queue_src", mksafe(request.equeue(id="queue", length=0.5)))
queue = cue_cut(queue)
queue = amplify(1., override="replay_gain", queue)
#fallback between queue and input.harbor (for restreaming other web-streams)
#live stream setup #live stream setup
set("harbor.bind_addr", "0.0.0.0") set("harbor.bind_addr", "0.0.0.0")
@ -43,6 +39,10 @@ just_switched = ref false
#web_stream = on_metadata(notify_stream, web_stream) #web_stream = on_metadata(notify_stream, web_stream)
#output.dummy(fallible=true, web_stream) #output.dummy(fallible=true, web_stream)
queue = audio_to_stereo(id="queue_src", request.equeue(id="queue", length=0.5))
queue = cue_cut(queue)
queue = amplify(1., override="replay_gain", queue)
# the crossfade function controls fade in/out # the crossfade function controls fade in/out
queue = crossfade_airtime(queue) queue = crossfade_airtime(queue)
queue = on_metadata(notify, queue) queue = on_metadata(notify, queue)
@ -53,11 +53,6 @@ http = input.http_restart(id="http")
http = cross_http(http_input_id="http",http) http = cross_http(http_input_id="http",http)
stream_queue = http_fallback(http_input_id="http",http=http,default=queue) stream_queue = http_fallback(http_input_id="http",http=http,default=queue)
#stream_queue = switch(id="stream_queue_switch", track_sensitive=false,
# transitions=[transition, transition],
# [({!webstream_enabled},web_stream),
# ({true}, queue)])
ignore(output.dummy(stream_queue, fallible=true)) ignore(output.dummy(stream_queue, fallible=true))
server.register(namespace="vars", server.register(namespace="vars",
@ -86,7 +81,9 @@ server.register(namespace="dynamic_source",
description="Enable webstream output", description="Enable webstream output",
usage='start', usage='start',
"output_start", "output_start",
fun (s) -> begin log("dynamic_source.output_start") webstream_enabled := true "enabled" end) fun (s) -> begin log("dynamic_source.output_start")
notify([("schedule_table_id", !current_dyn_id)])
webstream_enabled := true "enabled" end)
server.register(namespace="dynamic_source", server.register(namespace="dynamic_source",
description="Enable webstream output", description="Enable webstream output",
usage='stop', usage='stop',
@ -227,6 +224,7 @@ end
s = switch(id="default_switch", track_sensitive=false, s = switch(id="default_switch", track_sensitive=false,
transitions=[transition_default, transition], transitions=[transition_default, transition],
[({!scheduled_play_enabled}, stream_queue),({true},default)]) [({!scheduled_play_enabled}, stream_queue),({true},default)])
s = append_dj_inputs(master_live_stream_port, master_live_stream_mp, s = append_dj_inputs(master_live_stream_port, master_live_stream_mp,
dj_live_stream_port, dj_live_stream_mp, s) dj_live_stream_port, dj_live_stream_mp, s)