diff --git a/airtime_mvc/application/controllers/LibraryController.php b/airtime_mvc/application/controllers/LibraryController.php
index d258bd766..afa4baa7a 100644
--- a/airtime_mvc/application/controllers/LibraryController.php
+++ b/airtime_mvc/application/controllers/LibraryController.php
@@ -49,6 +49,7 @@ class LibraryController extends Zend_Controller_Action
$this->view->headLink()->appendStylesheet($baseUrl.'css/jquery.contextMenu.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'css/datatables/css/ColVis.css?'.$CC_CONFIG['airtime_version']);
$this->view->headLink()->appendStylesheet($baseUrl.'css/datatables/css/ColReorder.css?'.$CC_CONFIG['airtime_version']);
+ $this->view->headLink()->appendStylesheet($baseUrl.'css/waveform.css?'.$CC_CONFIG['airtime_version']);
$this->view->headScript()->appendFile($baseUrl.'js/airtime/library/spl.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
$this->view->headScript()->appendFile($baseUrl.'js/airtime/playlist/smart_blockbuilder.js?'.$CC_CONFIG['airtime_version'], 'text/javascript');
diff --git a/airtime_mvc/application/controllers/PlaylistController.php b/airtime_mvc/application/controllers/PlaylistController.php
index 1134499ce..2b48b40b7 100644
--- a/airtime_mvc/application/controllers/PlaylistController.php
+++ b/airtime_mvc/application/controllers/PlaylistController.php
@@ -421,8 +421,8 @@ class PlaylistController extends Zend_Controller_Action
public function setCrossfadeAction()
{
- $id1 = $this->_getParam('id1');
- $id2 = $this->_getParam('id2');
+ $id1 = $this->_getParam('id1', null);
+ $id2 = $this->_getParam('id2', null);
$type = $this->_getParam('type');
$fadeIn = $this->_getParam('fadeIn', 0);
$fadeOut = $this->_getParam('fadeOut', 0);
diff --git a/airtime_mvc/application/layouts/scripts/layout.phtml b/airtime_mvc/application/layouts/scripts/layout.phtml
index e67e429d6..1a050d3f2 100644
--- a/airtime_mvc/application/layouts/scripts/layout.phtml
+++ b/airtime_mvc/application/layouts/scripts/layout.phtml
@@ -38,22 +38,22 @@
-
Play
-
Stop
+
Play
+
Stop
+
-
-
-
-
-
-
-
-
@@ -62,14 +62,14 @@
diff --git a/airtime_mvc/application/models/Block.php b/airtime_mvc/application/models/Block.php
index bab3d3ed8..b1ac483a5 100644
--- a/airtime_mvc/application/models/Block.php
+++ b/airtime_mvc/application/models/Block.php
@@ -688,8 +688,12 @@ SQL;
$this->con->beginTransaction();
try {
- $this->changeFadeInfo($id1, null, $fadeOut);
- $this->changeFadeInfo($id2, $fadeIn, null, $offset);
+ if (isset($id1)) {
+ $this->changeFadeInfo($id1, null, $fadeOut);
+ }
+ if (isset($id2)) {
+ $this->changeFadeInfo($id2, $fadeIn, null, $offset);
+ }
$this->con->commit();
diff --git a/airtime_mvc/application/models/Playlist.php b/airtime_mvc/application/models/Playlist.php
index 16e89c7d5..aa74c78c4 100644
--- a/airtime_mvc/application/models/Playlist.php
+++ b/airtime_mvc/application/models/Playlist.php
@@ -670,8 +670,12 @@ SQL;
$this->con->beginTransaction();
try {
- $this->changeFadeInfo($id1, null, $fadeOut);
- $this->changeFadeInfo($id2, $fadeIn, null, $offset);
+ if (isset($id1)) {
+ $this->changeFadeInfo($id1, null, $fadeOut);
+ }
+ if (isset($id2)) {
+ $this->changeFadeInfo($id2, $fadeIn, null, $offset);
+ }
$this->con->commit();
diff --git a/airtime_mvc/application/models/Preference.php b/airtime_mvc/application/models/Preference.php
index 6cba9814c..b20aacd26 100644
--- a/airtime_mvc/application/models/Preference.php
+++ b/airtime_mvc/application/models/Preference.php
@@ -106,43 +106,44 @@ class Application_Model_Preference
private static function getValue($key, $isUserValue = false)
{
try {
- $con = Propel::getConnection();
-
+
//Check if key already exists
$sql = "SELECT COUNT(*) FROM cc_pref"
- ." WHERE keystr = '$key'";
- /*." WHERE keystr = :key";
+ ." WHERE keystr = :key";
+
$paramMap = array();
- $paramMap[':key'] = $key;*/
+ $paramMap[':key'] = $key;
+
//For user specific preference, check if id matches as well
if ($isUserValue) {
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
$id = $auth->getIdentity()->id;
- $sql .= " AND subjid = '$id'";
- /*$sql .= " AND subjid = :id";
- $paramMap[':id'] = $id;*/
+
+ $sql .= " AND subjid = :id";
+ $paramMap[':id'] = $id;
}
}
- $result = $con->query($sql)->fetchColumn(0);
- //$result = Application_Common_Database::prepareAndExecute($sql, $paramMap, 'column');
+
+ $result = Application_Common_Database::prepareAndExecute($sql, $paramMap, Application_Common_Database::COLUMN);
+
if ($result == 0) {
return "";
- } else {
+ }
+ else {
$sql = "SELECT valstr FROM cc_pref"
- ." WHERE keystr = '$key'";
- /*." WHERE keystr = :key";
+ ." WHERE keystr = :key";
+
$paramMap = array();
- $paramMap[':key'] = $key;*/
+ $paramMap[':key'] = $key;
//For user specific preference, check if id matches as well
if ($isUserValue && $auth->hasIdentity()) {
- $sql .= " AND subjid = '$id'";
- /*$sql .= " AND subjid = :id";
- $paramMap[':id'] = $id;*/
+ $sql .= " AND subjid = :id";
+ $paramMap[':id'] = $id;
}
- $result = $con->query($sql)->fetchColumn(0);
- //$result = Application_Common_Database::prepareAndExecute($sql, $paramMap, 'column');
+
+ $result = Application_Common_Database::prepareAndExecute($sql, $paramMap, Application_Common_Database::COLUMN);
return ($result !== false) ? $result : "";
}
@@ -609,9 +610,10 @@ class Application_Model_Preference
public static function GetCountryList()
{
- $con = Propel::getConnection();
$sql = "SELECT * FROM cc_country";
- $res = $con->query($sql)->fetchAll();
+
+ $res = Application_Common_Database::prepareAndExecute($sql, array());
+
$out = array();
$out[""] = _("Select Country");
foreach ($res as $r) {
diff --git a/airtime_mvc/application/views/scripts/playlist/set-fade.phtml b/airtime_mvc/application/views/scripts/playlist/set-fade.phtml
index 510ed441e..1bd1a74f6 100644
--- a/airtime_mvc/application/views/scripts/playlist/set-fade.phtml
+++ b/airtime_mvc/application/views/scripts/playlist/set-fade.phtml
@@ -11,7 +11,7 @@
item2Type == 0) {?>
+ if (isset($this->item2Url)) {?>
echo _("Fade in: "); ?> echo _("(ss.t)")?>
isStatic();
@@ -87,21 +88,33 @@ if (($i < count($items) -1) && ($items[$i+1]['type'] == 0)) {
if(($i < count($items) -1) && !($items[$i]['type'] == 2 && $items[$i+1]['type'])):
?>
- partial('playlist/set-fade.phtml', array(
+ $items[$i]['id'],
- 'item2' => $items[$i+1]['id'],
+ 'fadeOut' => $items[$i]['fadeout'],
+ 'fadeIn' => $items[$i+1]['fadein'],
'item1Type' => $items[$i]['type'],
- 'item2Type' => $items[$i+1]['type'],
- 'item1Url' => $fileUrl,
- 'item2Url' => $nextFileUrl,
- 'fadeOut' => $items[$i]['fadeout'],
- 'fadeIn' => $items[$i+1]['fadein'],
- 'offset' => $items[$i]['trackSec'] - $items[$i+1]['trackoffset'],
- 'cueIn1' => $items[$i]['cueInSec'],
+ 'cueIn1' => $items[$i]['cueInSec'],
'cueOut1' => $items[$i]['cueOutSec'],
+ 'item1Url' => $fileUrl
+ );
+
+ $item2 = array(
+ 'item2Url' => $nextFileUrl,
+ 'item2' => $items[$i+1]['id'],
+ 'item2Type' => $items[$i+1]['type'],
+ 'offset' => $items[$i]['trackSec'] - $items[$i+1]['trackoffset'],
'cueIn2' => $items[$i+1]['cueInSec'],
- 'cueOut2' => $items[$i+1]['cueOutSec'])
- ); ?>
+ 'cueOut2' => $items[$i+1]['cueOutSec']
+ );
+
+ if (isset($nextFileUrl)) {
+ $vars = $vars + $item2;
+ }
+
+ echo $this->partial('playlist/set-fade.phtml', $vars);
+ ?>
diff --git a/airtime_mvc/public/css/bootstrap.css b/airtime_mvc/public/css/bootstrap.css
index 97fa6b159..efe9abff7 100644
--- a/airtime_mvc/public/css/bootstrap.css
+++ b/airtime_mvc/public/css/bootstrap.css
@@ -190,13 +190,15 @@ a.badge:hover {
}
.btn.active,
.btn:active {
- background-color: #494949;
- background-color: #494949 \9;
+ background-color: #434343;
+ background-color: #434343 \9;
background-image: none;
outline: 0;
- -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
- -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
- box-shadow: inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05);
+ -webkit-box-shadow: inset 0 2px 3px rgba(0,0,0,.25), 0 1px 0 rgba(200,200,200,1);
+ -moz-box-shadow: inset 0 2px 3px rgba(0,0,0,.25), 0 1px 0 rgba(200,200,200,1);
+ box-shadow: inset 0 2px 3px rgba(0,0,0,.25), 0 1px 0 rgba(220,220,220,1);
+ border: 1px solid #131313;
+ color: #a5a5a5 !important ;
}
.btn.disabled,
.btn[disabled],
diff --git a/airtime_mvc/public/css/playlist_builder.css b/airtime_mvc/public/css/playlist_builder.css
index 7d828bd31..20da9dafa 100644
--- a/airtime_mvc/public/css/playlist_builder.css
+++ b/airtime_mvc/public/css/playlist_builder.css
@@ -584,29 +584,4 @@ li.spl_empty {
}
.expand-block-separate {
border-top: 1px solid #5B5B5B;
-}
-
-.channel-wrapper {
- position: relative;
-}
-
-.channel {
- position: absolute;
- margin: 0;
- padding: 0;
-}
-
-.state-select {
- cursor: text;
-}
-
-.playlist-tracks {
- overflow-x: auto;
- overflow-y: hidden;
-}
-
-.playlist-fade {
- position: absolute;
- background-color: rgba(0,0,0,0.1);
- z-index: 1000;
}
\ No newline at end of file
diff --git a/airtime_mvc/public/css/waveform.css b/airtime_mvc/public/css/waveform.css
new file mode 100644
index 000000000..fd4c9bd57
--- /dev/null
+++ b/airtime_mvc/public/css/waveform.css
@@ -0,0 +1,74 @@
+.ui-dialog .ui-dialog-content {
+ padding: 0px;
+}
+.ui-dialog .ui-dialog-buttonpane {
+ padding: 0.3em 0.2em 0 0.4em;
+ margin: 0 -0.4em 0;
+}
+.btn-small [class^="icon-"] {
+ margin: 1px 5px 0 -3px;
+}
+.btn {
+ margin-right: 3px;
+}
+.btn-small {
+ /*line-height: 20px;*/
+}
+.playlist-tracks {
+ margin: 8px 0;
+}
+.playlist-controls {
+ margin: 0 0 16px 0;
+}
+
+.channel-wrapper {
+ position: relative;
+}
+
+.channel {
+ position: absolute;
+ margin: 0;
+ padding: 0;
+ background: #3e3e3e;
+}
+
+.state-select {
+ cursor: text;
+}
+
+.playlist-tracks {
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.playlist-fade {
+ position: absolute;
+ background-color: rgba(0,0,0,0.1);
+ z-index: 1000;
+}
+.set-cue {
+ margin: 12px 0 16px 0;
+}
+.set-fade {
+ margin: 0 0 0 30px;
+}
+.set-cue input[type="text"] {
+ vertical-align: middle;
+ padding: 5px 3px 4px 3px;
+}
+.set-cue input[type="button"] {
+ min-width: 100px;
+ margin-top: 0px;
+ margin-left: 4px;
+}
+label {
+ color:#333;
+ padding: 0 5px 0 6px;
+ display: inline-block;
+ min-width: 50px;
+ text-align: right;
+}
+label.audio {
+ font-weight:bold;
+ vertical-align: middle;
+}
\ No newline at end of file
diff --git a/airtime_mvc/public/js/airtime/library/spl.js b/airtime_mvc/public/js/airtime/library/spl.js
index 18d10ba0e..b15f56540 100644
--- a/airtime_mvc/public/js/airtime/library/spl.js
+++ b/airtime_mvc/public/js/airtime/library/spl.js
@@ -1223,8 +1223,8 @@ var AIRTIME = (function(AIRTIME){
width: dim.width - 100,
height: dim.height - 100,
buttons: [
- {text: "Cancel", click: removeDialog},
- {text: "Save", click: function() {
+ {text: "Cancel", class: "btn btn-small", click: removeDialog},
+ {text: "Save", class: "btn btn-small btn-inverse", click: function() {
var json = playlistEditor.getJson(),
offset,
fadeIn, fadeOut,
@@ -1254,7 +1254,10 @@ var AIRTIME = (function(AIRTIME){
fadeIn = fade["end"] - fade["start"];
}
- changeCrossfade($html, id1, id2, fadeIn.toFixed(1), fadeOut.toFixed(1), offset);
+ fadeIn = (fadeIn === undefined) ? undefined : fadeIn.toFixed(1);
+ fadeOut = (fadeOut === undefined) ? undefined : fadeOut.toFixed(1);
+
+ changeCrossfade($html, id1, id2, fadeIn, fadeOut, offset);
}}
],
open: function (event, ui) {
@@ -1305,19 +1308,19 @@ var AIRTIME = (function(AIRTIME){
$html.remove();
}
- $html.find('.editor-cue-in').val(cueIn);
- $html.find('.editor-cue-out').val(cueOut);
+ $html.find('.editor-cue-in').html(cueIn);
+ $html.find('.editor-cue-out').html(cueOut);
$html.on("click", ".set-cue-in", function(e) {
var cueIn = $html.find('.audio_start').val();
- $html.find('.editor-cue-in').val(cueIn);
+ $html.find('.editor-cue-in').html(cueIn);
});
$html.on("click", ".set-cue-out", function(e) {
var cueOut = $html.find('.audio_end').val();
- $html.find('.editor-cue-out').val(cueOut);
+ $html.find('.editor-cue-out').html(cueOut);
});
$html.dialog({
@@ -1328,10 +1331,10 @@ var AIRTIME = (function(AIRTIME){
width: dim.width - 100,
height: dim.height - 100,
buttons: [
- {text: "Cancel", click: removeDialog},
- {text: "Save", click: function() {
- var cueIn = $html.find('.editor-cue-in').val(),
- cueOut = $html.find('.editor-cue-out').val();
+ {text: "Cancel", class: "btn btn-small", click: removeDialog},
+ {text: "Save", class: "btn btn-small btn-inverse", click: function() {
+ var cueIn = $html.find('.editor-cue-in').html(),
+ cueOut = $html.find('.editor-cue-out').html();
playlistEditor.stop();
diff --git a/airtime_mvc/public/js/waveformplaylist/controls.js b/airtime_mvc/public/js/waveformplaylist/controls.js
index d83af02f0..1a41aa23e 100644
--- a/airtime_mvc/public/js/waveformplaylist/controls.js
+++ b/airtime_mvc/public/js/waveformplaylist/controls.js
@@ -556,7 +556,7 @@ AudioControls.prototype.onCursorSelection = function(args) {
*/
AudioControls.prototype.onAudioUpdate = function(args) {
if (this.ctrls["audio_pos"]) {
- this.ctrls["audio_pos"].value = this.cueFormatters(this.timeFormat)(args.seconds);
+ this.ctrls["audio_pos"].innerHTML = this.cueFormatters(this.timeFormat)(args.seconds);
}
};
diff --git a/python_apps/pypo/liquidsoap_scripts/ls_script.liq b/python_apps/pypo/liquidsoap_scripts/ls_script.liq
index 58a91909b..97dc544c5 100644
--- a/python_apps/pypo/liquidsoap_scripts/ls_script.liq
+++ b/python_apps/pypo/liquidsoap_scripts/ls_script.liq
@@ -231,6 +231,7 @@ s = switch(id="schedule_noise_switch",
s = if dj_live_stream_port != 0 and dj_live_stream_mp != "" then
dj_live = mksafe(
+ id="dj_live_mksafe",
audio_to_stereo(
input.harbor(id="live_dj_harbor",
dj_live_stream_mp,
@@ -253,14 +254,15 @@ end
s = if master_live_stream_port != 0 and master_live_stream_mp != "" then
master_dj = mksafe(
- audio_to_stereo(
- input.harbor(id="master_harbor",
- master_live_stream_mp,
- port=master_live_stream_port,
- auth=check_master_dj_client,
- max=40.,
- on_connect=master_dj_connect,
- on_disconnect=master_dj_disconnect)))
+ id="master_dj_mksafe",
+ audio_to_stereo(
+ input.harbor(id="master_harbor",
+ master_live_stream_mp,
+ port=master_live_stream_port,
+ auth=check_master_dj_client,
+ max=40.,
+ on_connect=master_dj_connect,
+ on_disconnect=master_dj_disconnect)))
ignore(output.dummy(master_dj, fallible=true))
diff --git a/python_apps/pypo/pure.py b/python_apps/pypo/pure.py
new file mode 100644
index 000000000..8b004ae6d
--- /dev/null
+++ b/python_apps/pypo/pure.py
@@ -0,0 +1,7 @@
+import re
+
+
+def version_cmp(version1, version2):
+ def normalize(v):
+ return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")]
+ return cmp(normalize(version1), normalize(version2))
diff --git a/python_apps/pypo/pypocli.py b/python_apps/pypo/pypocli.py
index 49582bfec..42d3205b4 100644
--- a/python_apps/pypo/pypocli.py
+++ b/python_apps/pypo/pypocli.py
@@ -13,6 +13,7 @@ import signal
import logging
import locale
import os
+import re
from Queue import Queue
from threading import Lock
@@ -33,6 +34,7 @@ from configobj import ConfigObj
# custom imports
from api_clients import api_client
from std_err_override import LogWriter
+import pure
# Set up command-line options
parser = OptionParser()
@@ -71,6 +73,8 @@ parser.add_option("-c",
# parse options
(options, args) = parser.parse_args()
+LIQUIDSOAP_MIN_VERSION = "1.1.1"
+
#need to wait for Python 2.7 for this..
#logging.captureWarnings(True)
@@ -152,7 +156,7 @@ def keyboardInterruptHandler(signum, frame):
logger.info('\nKeyboard Interrupt\n')
sys.exit(0)
-def liquidsoap_running_test(telnet_lock, host, port, logger):
+def liquidsoap_get_info(telnet_lock, host, port, logger):
logger.debug("Checking to see if Liquidsoap is running")
try:
telnet_lock.acquire()
@@ -161,14 +165,47 @@ def liquidsoap_running_test(telnet_lock, host, port, logger):
tn.write(msg)
tn.write("exit\n")
response = tn.read_all()
- logger.info("Found: %s", response)
except Exception, e:
logger.error(str(e))
- return False
+ return None
finally:
telnet_lock.release()
- return "Liquidsoap" in response
+ return get_liquidsoap_version(response)
+
+def get_liquidsoap_version(version_string):
+ m = re.match(r"Liquidsoap (\d+.\d+.\d+)", "Liquidsoap 1.1.1")
+
+ if m:
+ return m.group(1)
+ else:
+ return None
+
+
+ if m:
+ current_version = m.group(1)
+ return pure.version_cmp(current_version, LIQUIDSOAP_MIN_VERSION) >= 0
+ return False
+
+def liquidsoap_startup_test():
+
+ liquidsoap_version_string = \
+ liquidsoap_get_info(telnet_lock, ls_host, ls_port, logger)
+ while not liquidsoap_version_string:
+ logger.warning("Liquidsoap doesn't appear to be running!, " + \
+ "Sleeping and trying again")
+ time.sleep(1)
+ liquidsoap_version_string = \
+ liquidsoap_get_info(telnet_lock, ls_host, ls_port, logger)
+
+ while pure.version_cmp(liquidsoap_version_string, LIQUIDSOAP_MIN_VERSION) < 0:
+ logger.warning("Liquidsoap is running but in incorrect version! " + \
+ "Make sure you have at least Liquidsoap %s installed" % LIQUIDSOAP_MIN_VERSION)
+ time.sleep(1)
+ liquidsoap_version_string = \
+ liquidsoap_get_info(telnet_lock, ls_host, ls_port, logger)
+
+ logger.info("Liquidsoap version string found %s" % liquidsoap_version_string)
if __name__ == '__main__':
@@ -204,9 +241,8 @@ if __name__ == '__main__':
ls_host = config['ls_host']
ls_port = config['ls_port']
- while not liquidsoap_running_test(telnet_lock, ls_host, ls_port, logger):
- logger.warning("Liquidsoap not started yet. Sleeping one second and trying again")
- time.sleep(1)
+
+ liquidsoap_startup_test()
if options.test:
g.test_api()
diff --git a/python_apps/pypo/pypofetch.py b/python_apps/pypo/pypofetch.py
index 38a72f363..d5959e502 100644
--- a/python_apps/pypo/pypofetch.py
+++ b/python_apps/pypo/pypofetch.py
@@ -528,10 +528,6 @@ class PypoFetch(Thread):
def main(self):
- # Bootstrap: since we are just starting up, we need to grab the
- # most recent schedule. After that we can just wait for updates.
- success = self.persistent_manual_schedule_fetch(max_attempts=5)
-
#Make sure all Liquidsoap queues are empty. This is important in the
#case where we've just restarted the pypo scheduler, but Liquidsoap still
#is playing tracks. In this case let's just restart everything from scratch
@@ -539,6 +535,10 @@ class PypoFetch(Thread):
#Liquidsoap is playing much more easily.
self.pypo_liquidsoap.clear_all_queues()
+ # Bootstrap: since we are just starting up, we need to grab the
+ # most recent schedule. After that we can just wait for updates.
+ success = self.persistent_manual_schedule_fetch(max_attempts=5)
+
if success:
self.logger.info("Bootstrap schedule received: %s", self.schedule_data)
self.set_bootstrap_variables()