diff --git a/airtime_mvc/application/common/DateHelper.php b/airtime_mvc/application/common/DateHelper.php index c13543d8e..a22a26b4a 100644 --- a/airtime_mvc/application/common/DateHelper.php +++ b/airtime_mvc/application/common/DateHelper.php @@ -48,7 +48,7 @@ class Application_Common_DateHelper /** * Get the week start date of this week in the format * YYYY-MM-DD - * + * * @return String - week start date */ function getWeekStartDate() @@ -231,7 +231,7 @@ class Application_Common_DateHelper if (2 !== substr_count($p_time, ":")){ return FALSE; } - + if (1 === substr_count($p_time, ".")){ list($hhmmss, $ms) = explode(".", $p_time); } else { @@ -275,17 +275,17 @@ class Application_Common_DateHelper 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 - * then the current time is provided. - * + * then the current time is provided. + * * @param $p_dateString * Date string in UTC timezone. * @param $p_format * Format which the string should be returned in. - * - * @return string - * Date String in localtime + * + * @return string + * Date String in localtime * */ public static function ConvertToLocalDateTimeString($p_dateString, $p_format="Y-m-d H:i:s"){ 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 * representing that 2 minute, 32.746562 second interval. - * + * */ public static function getDateIntervalFromString($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"; } else if (!checkdate($month, $day, $year)) { $retVal['success'] = false; - $retVal['errMsg'] = "'$year-$month-$day' is not a valid date"; + $retVal['errMsg'] = "'$year-$month-$day' is not a valid date"; } else { // check time if (isset($timeInfo)) { diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index 1a1876ce9..89fd93467 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -926,31 +926,33 @@ class ApiController extends Zend_Controller_Action $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() - ->orderByDbStartTime('desc') - ->filterByDbInstanceId($media_id) - ->findOne(); + $previous_metadata = CcWebstreamMetadataQuery::create() + ->orderByDbStartTime('desc') + ->filterByDbInstanceId($media_id) + ->findOne(); - $do_insert = true; - if ($previous_metadata) { - if ($previous_metadata->getDbLiquidsoapData() == $data_arr->title) { - Logging::debug("Duplicate found: ".$data_arr->title); - $do_insert = false; + $do_insert = true; + if ($previous_metadata) { + if ($previous_metadata->getDbLiquidsoapData() == $data_arr->title) { + Logging::debug("Duplicate found: ".$data_arr->title); + $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 { - 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; diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index f4acdf886..bc98168d3 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -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 - * schedule array that was just built and fix the data up a bit. For - * example, if we have two consecutive webstreams, we don't need the - * first webstream to shutdown the output, when the second one will - * just switch it back on. Preventing this behaviour stops hiccups - * in output sound. + * Streams are a 4 stage process. + * 1) start buffering stream 5 seconds ahead of its start time + * 2) at the start time tell liquidsoap to switch to this source + * 3) at the end time, tell liquidsoap to stop reading this stream + * 4) at the end time, tell liquidsoap to switch away from input.http source. + * + * 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_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) { - 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]); + 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_val = $v; } @@ -870,7 +904,7 @@ SQL; self::createInputHarborKickTimes($data, $range_start, $range_end); self::createScheduledEvents($data, $range_start, $range_end); - self::filterData($data["media"]); + self::foldData($data["media"]); return $data; } diff --git a/airtime_mvc/application/models/ShowBuilder.php b/airtime_mvc/application/models/ShowBuilder.php index 789d5990a..175e61dfd 100644 --- a/airtime_mvc/application/models/ShowBuilder.php +++ b/airtime_mvc/application/models/ShowBuilder.php @@ -266,8 +266,12 @@ class Application_Model_ShowBuilder $row["instance"] = intval($p_item["si_id"]); $row["starts"] = $schedStartDT->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["title"] = $p_item["file_track_title"]; diff --git a/airtime_mvc/application/models/Webstream.php b/airtime_mvc/application/models/Webstream.php index ef3f32605..ad6ca7f76 100644 --- a/airtime_mvc/application/models/Webstream.php +++ b/airtime_mvc/application/models/Webstream.php @@ -34,7 +34,7 @@ class Application_Model_Webstream implements Application_Model_LibraryEditable public function getCreatorId() { - return $this->Webstream->getCcSubjs()->getDbId(); + return $this->webstream->getDbCreatorId(); } 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"); return $di->format("%Hh %Im"); - } + } return ""; } diff --git a/airtime_mvc/public/js/airtime/audiopreview/preview_jplayer.js b/airtime_mvc/public/js/airtime/audiopreview/preview_jplayer.js index e7435ee2e..a18f31d5d 100644 --- a/airtime_mvc/public/js/airtime/audiopreview/preview_jplayer.js +++ b/airtime_mvc/public/js/airtime/audiopreview/preview_jplayer.js @@ -153,12 +153,24 @@ function buildplaylist(p_url, p_playIndex) { continue; } } else if (data[index]['type'] == 1) { - media = {title: data[index]['element_title'], - artist: data[index]['element_artist'], - mp3:data[index]['uri'] - }; + var mime = data[index]['mime']; + if (mime.search(/mp3/i) > 0 || mime.search(/mpeg/i) > 0) { + 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'])) { // javascript doesn't support associative array with numeric key // so we need to remove the gap if we skip any of tracks due to diff --git a/airtime_mvc/public/js/airtime/common/audioplaytest.js b/airtime_mvc/public/js/airtime/common/audioplaytest.js index f96ad09af..078d8f1e4 100644 --- a/airtime_mvc/public/js/airtime/common/audioplaytest.js +++ b/airtime_mvc/public/js/airtime/common/audioplaytest.js @@ -15,5 +15,6 @@ function isAudioSupported(mime){ //is adding a javascript library to do the work for you, which seems like overkill.... return (!!audio.canPlayType && audio.canPlayType(bMime) != "") || (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); } diff --git a/airtime_mvc/public/js/airtime/library/library.js b/airtime_mvc/public/js/airtime/library/library.js index b463610bc..144ce7e9c 100644 --- a/airtime_mvc/public/js/airtime/library/library.js +++ b/airtime_mvc/public/js/airtime/library/library.js @@ -975,7 +975,6 @@ function addProgressIcon(id) { } function checkLibrarySCUploadStatus(){ - var url = '/Library/get-upload-to-soundcloud-status', span, id; @@ -1028,15 +1027,22 @@ function addQtipToSCIcons(){ }); } else if($(this).hasClass("soundcloud")){ + var sc_id = $(this).parent().parent().data("aData").soundcloud_id; $(this).qtip({ content: { + //text: "The soundcloud id for this file is: "+sc_id text: "Retrieving data from the server...", ajax: { url: "/Library/get-upload-to-soundcloud-status", type: "post", data: ({format: "json", id : id, type: "file"}), 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); } } }, diff --git a/python_apps/pypo/liquidsoap_scripts/ls_script.liq b/python_apps/pypo/liquidsoap_scripts/ls_script.liq index 5cec2301b..3a9468512 100644 --- a/python_apps/pypo/liquidsoap_scripts/ls_script.liq +++ b/python_apps/pypo/liquidsoap_scripts/ls_script.liq @@ -11,11 +11,7 @@ set("server.telnet.port", 1234) webstream_enabled = ref false 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 set("harbor.bind_addr", "0.0.0.0") @@ -43,6 +39,10 @@ just_switched = ref false #web_stream = on_metadata(notify_stream, 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 queue = crossfade_airtime(queue) queue = on_metadata(notify, queue) @@ -53,11 +53,6 @@ http = input.http_restart(id="http") http = cross_http(http_input_id="http",http) 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)) server.register(namespace="vars", @@ -86,7 +81,9 @@ server.register(namespace="dynamic_source", description="Enable webstream output", usage='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", description="Enable webstream output", usage='stop', @@ -227,6 +224,7 @@ end s = switch(id="default_switch", track_sensitive=false, transitions=[transition_default, transition], [({!scheduled_play_enabled}, stream_queue),({true},default)]) + s = append_dj_inputs(master_live_stream_port, master_live_stream_mp, dj_live_stream_port, dj_live_stream_mp, s)