\ No newline at end of file
diff --git a/airtime_mvc/application/views/scripts/form/stream_setting.phtml b/airtime_mvc/application/views/scripts/form/stream_setting.phtml
deleted file mode 100644
index dfec45ef5..000000000
--- a/airtime_mvc/application/views/scripts/form/stream_setting.phtml
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/airtime_mvc/application/views/scripts/preference/stream-setting.phtml b/airtime_mvc/application/views/scripts/preference/stream-setting.phtml
index bdbb30267..f28215968 100644
--- a/airtime_mvc/application/views/scripts/preference/stream-setting.phtml
+++ b/airtime_mvc/application/views/scripts/preference/stream-setting.phtml
@@ -1,4 +1,25 @@
-
-
Stream Setting
-form; ?>
-
+
+
Stream configuration
+statusMsg;?>
+
+
\ No newline at end of file
diff --git a/airtime_mvc/build/sql/defaultdata.sql b/airtime_mvc/build/sql/defaultdata.sql
index 49d1b395a..e29a60543 100644
--- a/airtime_mvc/build/sql/defaultdata.sql
+++ b/airtime_mvc/build/sql/defaultdata.sql
@@ -1,5 +1,8 @@
INSERT INTO cc_subjs ("login", "type", "pass") VALUES ('admin', 'A', md5('admin'));
+INSERT INTO cc_pref("keystr", "valstr") VALUES('stream_type', 'mp3, ogg');
+INSERT INTO cc_pref("keystr", "valstr") VALUES('stream_bitrate', '24, 32, 48, 64, 96, 128, 160, 192, 224, 256, 320');
+
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('output_sound_device', 'false', 'boolean');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('output_icecast_vorbis_metadata', 'false', 'boolean');
INSERT INTO cc_stream_setting ("keyname", "value", "type") VALUES ('output_s1', 'icecast', 'string');
diff --git a/airtime_mvc/public/css/styles.css b/airtime_mvc/public/css/styles.css
index 2c801123e..d12dfb8e8 100644
--- a/airtime_mvc/public/css/styles.css
+++ b/airtime_mvc/public/css/styles.css
@@ -1247,7 +1247,7 @@ div.success{
border:1px solid #488214;
}
-.collapsible-header {
+.collapsible-header, .collapsible-header-disabled {
border: 1px solid #8f8f8f;
background-color: #cccccc;
background: -moz-linear-gradient(top, #cccccc 0, #b9b9b9 100%);
@@ -1264,7 +1264,7 @@ div.success{
margin-top:-1px;
display:none;
}
-.collapsible-header .arrow-icon {
+.collapsible-header .arrow-icon, .collapsible-header-disabled .arrow-icon {
display:block;
background:url(images/arrows_collapse.png) no-repeat 0 0;
height:11px;
@@ -1274,7 +1274,7 @@ div.success{
top:8px;
}
-.collapsible-header.close .arrow-icon {
+.collapsible-header.close .arrow-icon, collapsible-header-disabled.close .arrow-icon {
background-position: 0 -11px;
}
@@ -1604,7 +1604,7 @@ div.success{
.medium-icon.finishedplaying {
background:url(images/icon_finishedplaying_m.png) no-repeat 0 0;
}
-.preferences, .manage-folders {
+.preferences, .manage-folders, .stream-config {
width: 500px;
}
@@ -1614,46 +1614,55 @@ dt.block-display, dd.block-display {
margin-left: 0;
padding-left: 0;
}
-.preferences dt.block-display, .preferences dd.block-display {
+.preferences dt.block-display, .preferences dd.block-display,
+.stream-config dt.block-display, .stream-config dd.block-display {
padding: 0 0 5px 0;
}
-.preferences dd.block-display {
+.preferences dd.block-display, .stream-config dd.block-display {
margin-bottom:4px;
}
-.preferences dd.block-display:last-child {
+.preferences dd.block-display:last-child, .stream-config dd.block-display:last-child {
margin-bottom:0;
+ padding-bottom:0;
}
.simple-formblock dd.block-display {
width: 100%;
}
-.preferences input[type="radio"] {
+.preferences input[type="radio"], .stream-config input[type="radio"] {
margin:0;
}
-.preferences label input[type="radio"] {
+.preferences label input[type="radio"], .stream-config label input[type="radio"] {
margin:0 1px 0 0;
}
-.preferences label input[type="checkbox"] {
+.preferences label input[type="checkbox"], .stream-config label input[type="checkbox"] {
margin:0 5px 0 0;
}
-dd.radio-inline-list, .preferences dd.radio-inline-list {
+dd.radio-inline-list, .preferences dd.radio-inline-list, .stream-config dd.radio-inline-list {
margin-bottom:6px;
}
.radio-inline-list label {
margin-right:12px;
}
-.preferences.simple-formblock dd.block-display {
+.preferences.simple-formblock dd.block-display, .stream-config.simple-formblock dd.block-display {
width: 100%;
}
-.preferences.simple-formblock dd.block-display select {
+.preferences.simple-formblock dd.block-display select, .stream-config.simple-formblock dd.block-display select {
width: 100%;
}
-.preferences dd.block-display .input_select {
+.preferences dd.block-display .input_select, .stream-config dd.block-display .input_select {
width: 100%;
}
-.preferences dd.block-display .input_text_area, .preferences dd.block-display .input_text {
+.preferences dd.block-display .input_text_area, .preferences dd.block-display .input_text,
+.stream-config dd.block-display .input_text_area, .stream-config dd.block-display .input_text,
+.stream-config dd.block-display input[type="text"], .stream-config dd.block-display input[type="password"] {
width: 99.5%;
}
+
+
+
+
+
.preferences dd#SoundCloudTags-element.block-display .input_text_area {
height: 120px;
}
@@ -1872,7 +1881,7 @@ fieldset legend .ui-icon, .ui-widget-content fieldset legend .ui-icon {
input[type="checkbox"][disabled] {
opacity: 0.6;
}
-
+
.play_small {
height:11px;
width: 15px;
@@ -1909,4 +1918,54 @@ input[type="checkbox"][disabled] {
dd .info-text-small {
padding: 1px 0 2px;
display:inline-block;
+}
+
+.stream-config dt {
+ width:120px;
+ padding: 4px 0;
+}
+.stream-config dt.block-display {
+ width:auto;
+}
+.stream-config dd {
+ margin-bottom:0px;
+}
+.stream-config dd select {
+ width:160px;
+ line-height:140%;
+}
+
+dt.block-display.info-block {
+ width: auto;
+ font-size:12px;
+ padding:10px 0;
+}
+.top-margin {
+ margin-top:10px;
+}
+.left-margin {
+ margin-left:20px;
+}
+.stream-config dd.block-display textarea {
+ width: 99.5%;
+ height: 110px;
+}
+
+.input-info {
+ font-size:12px;
+ padding:0 0 0 5px;
+}
+
+.stream-config dd.block-display input[type="text"].with-info, .stream-config dd.block-display input[type="password"].with-info {
+ width: 85%;
+}
+.stream-config dd.block-display p {
+ font-size:13px;
+ margin:4px 0 4px 2px;
+}
+
+.collapsible-header-disabled {
+ cursor:default;
+ opacity:0.6;
+
}
\ No newline at end of file
diff --git a/airtime_mvc/public/js/airtime/preferences/preferences.js b/airtime_mvc/public/js/airtime/preferences/preferences.js
index 8a77bb68a..b9f8818b2 100644
--- a/airtime_mvc/public/js/airtime/preferences/preferences.js
+++ b/airtime_mvc/public/js/airtime/preferences/preferences.js
@@ -1,6 +1,6 @@
function showErrorSections() {
- if($("soundcloud-settings .errors").length > 0) {
+ if($("#soundcloud-settings .errors").length > 0) {
$("#soundcloud-settings").show();
$(window).scrollTop($("soundcloud-settings .errors").position().top);
}
diff --git a/airtime_mvc/public/js/airtime/preferences/streamsetting.js b/airtime_mvc/public/js/airtime/preferences/streamsetting.js
index 69eb6f11b..21a0195a5 100644
--- a/airtime_mvc/public/js/airtime/preferences/streamsetting.js
+++ b/airtime_mvc/public/js/airtime/preferences/streamsetting.js
@@ -1,13 +1,38 @@
-$(document).ready(function() {
+function showErrorSections() {
- $('#change_setting').click(function(){
- var url;
-
- url = "/Preference/change-stream-setting";
-
- $.post(url,
- {format: "json"}
- );
+ $(".errors").each(function(i){
+ if($(this).length > 0){
+ $(this).closest("div").show();
+ $(window).scrollTop($(this).position().top);
+ return false;
+ }
});
+}
+function buildStreamUrl(){
+
+ $("input:[id$=-host], input:[id$=-port], input:[id$=-mount], select:[id$=-type]").change(function(){
+ div = $(this).closest("div")
+ host = div.find("input:[id$=-host]").val()
+ port = div.find("input:[id$=-port]").val()
+ mount = div.find("input:[id$=-mount]").val()
+ type = div.find("select:[id$=-type]").val()
+ div.find("#stream_url").html("http://"+host+":"+port+"/"+mount+"."+type)
+ if($(this).attr('id').indexOf('type') != -1){
+ div.find("#mount_ext").html("."+type)
+ }
+ })
+}
+
+$(document).ready(function() {
+
+ $('.collapsible-header').click(function() {
+ $(this).next().toggle('fast');
+ $(this).toggleClass("close");
+ return false;
+ }).next().hide();
+
+ showErrorSections()
+
+ buildStreamUrl()
});
\ No newline at end of file
diff --git a/python_apps/pypo/airtime-playout-init-d b/python_apps/pypo/airtime-playout-init-d
index dabc0dac5..f6a9f499c 100755
--- a/python_apps/pypo/airtime-playout-init-d
+++ b/python_apps/pypo/airtime-playout-init-d
@@ -19,13 +19,24 @@ PIDFILE0=/var/run/airtime-playout.pid
DAEMON1=/usr/lib/airtime/pypo/bin/airtime-liquidsoap
PIDFILE1=/var/run/airtime-liquidsoap.pid
+liquidsoap_start () {
+ monit monitor airtime-liquidsoap >/dev/null 2>&1
+ start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID \
+ --nicelevel -15 --make-pidfile --pidfile $PIDFILE1 --startas $DAEMON1
+}
+
+liquidsoap_stop () {
+ # Send TERM after 5 seconds, wait at most 30 seconds.
+ monit unmonitor airtime-liquidsoap >/dev/null 2>&1
+ start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE1
+ rm -f $PIDFILE1
+}
+
start () {
monit monitor airtime-playout >/dev/null 2>&1
start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID --make-pidfile --pidfile $PIDFILE0 --startas $DAEMON0
- monit monitor airtime-liquidsoap >/dev/null 2>&1
- start-stop-daemon --start --background --quiet --chuid $USERID:$GROUPID \
- --nicelevel -15 --make-pidfile --pidfile $PIDFILE1 --startas $DAEMON1
+ liquidsoap_start
}
stop () {
@@ -35,12 +46,9 @@ stop () {
start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE0
rm -f $PIDFILE0
- monit unmonitor airtime-liquidsoap >/dev/null 2>&1
- start-stop-daemon --stop --oknodo --retry TERM/5/0/30 --quiet --pidfile $PIDFILE1
- rm -f $PIDFILE1
+ liquidsoap_stop
}
-
case "${1:-''}" in
'start')
# start commands here
@@ -65,6 +73,13 @@ case "${1:-''}" in
# status commands here
/usr/bin/airtime-check-system
;;
+ 'restart-liquidsoap')
+ # restart commands here
+ echo -n "Restarting Liquidsoap: "
+ liquidsoap_stop
+ liquidsoap_start
+ echo "Done."
+ ;;
*) # no parameter specified
echo "Usage: $SELF start|stop|restart|status"
exit 1
diff --git a/python_apps/pypo/install/pypo-install.py b/python_apps/pypo/install/pypo-install.py
index feb030624..fcb4e7b81 100755
--- a/python_apps/pypo/install/pypo-install.py
+++ b/python_apps/pypo/install/pypo-install.py
@@ -114,26 +114,28 @@ try:
p = Popen("update-rc.d airtime-playout defaults >/dev/null 2>&1", shell=True)
sts = os.waitpid(p.pid, 0)[1]
- #we should access the DB and generate liquidsoap.cfg under etc/airtime/
+ # we should access the DB and generate liquidsoap.cfg under etc/airtime/
api_client = api_client.api_client_factory(config)
ss = api_client.get_stream_setting()
- data = ss['msg']
- fh = open('/etc/airtime/liquidsoap.cfg', 'w')
- for d in data:
- buffer = d[u'keyname'] + " = "
- if(d[u'type'] == 'string'):
- temp = d[u'value']
- if(temp == ""):
- temp = "dummy_string"
- buffer += "\"" + temp + "\""
- else:
- temp = d[u'value']
- if(temp == ""):
- temp = "0"
- buffer += temp
- buffer += "\n"
- fh.write(buffer)
- fh.close()
+ # if api_client is somehow not working, just use original cfg file
+ if(ss is not None):
+ data = ss['msg']
+ fh = open('/etc/airtime/liquidsoap.cfg', 'w')
+ for d in data:
+ buffer = d[u'keyname'] + " = "
+ if(d[u'type'] == 'string'):
+ temp = d[u'value']
+ if(temp == ""):
+ temp = ""
+ buffer += "\"" + temp + "\""
+ else:
+ temp = d[u'value']
+ if(temp == ""):
+ temp = "0"
+ buffer += temp
+ buffer += "\n"
+ fh.write(buffer)
+ fh.close()
print "Waiting for processes to start..."
p = Popen("/etc/init.d/airtime-playout start", shell=True)
diff --git a/python_apps/pypo/liquidsoap_scripts/ls_lib.liq b/python_apps/pypo/liquidsoap_scripts/ls_lib.liq
index 71ed8dc9f..e5cffa293 100644
--- a/python_apps/pypo/liquidsoap_scripts/ls_lib.liq
+++ b/python_apps/pypo/liquidsoap_scripts/ls_lib.liq
@@ -50,8 +50,12 @@ def output_to(output_type, type, bitrate, host, port, pass, mount_point, url, de
description = description,
genre = genre)
if type == "mp3" then
- if bitrate == 32 then
+ if bitrate == 24 then
+ ignore(output.icecast(%mp3(bitrate = 24),s))
+ elsif bitrate == 32 then
ignore(output.icecast(%mp3(bitrate = 32),s))
+ elsif bitrate == 48 then
+ ignore(output.icecast(%mp3(bitrate = 48),s))
elsif bitrate == 64 then
ignore(output.icecast(%mp3(bitrate = 64),s))
elsif bitrate == 96 then
@@ -60,14 +64,26 @@ def output_to(output_type, type, bitrate, host, port, pass, mount_point, url, de
ignore(output.icecast(%mp3(bitrate = 128),s))
elsif bitrate == 160 then
ignore(output.icecast(%mp3(bitrate = 160),s))
+ elsif bitrate == 192 then
+ ignore(output.icecast(%mp3(bitrate = 192),s))
+ elsif bitrate == 224 then
+ ignore(output.icecast(%mp3(bitrate = 224),s))
+ elsif bitrate == 256 then
+ ignore(output.icecast(%mp3(bitrate = 256),s))
+ elsif bitrate == 320 then
+ ignore(output.icecast(%mp3(bitrate = 320),s))
end
else
source = ref s
if not output_icecast_vorbis_metadata then
source := add(normalize=false, [amplify(0.00001, noise()),s])
end
- if bitrate == 32 then
+ if bitrate == 24 then
+ ignore(output.icecast(%vorbis.cbr(bitrate = 24),!source))
+ elsif bitrate == 32 then
ignore(output.icecast(%vorbis.cbr(bitrate = 32),!source))
+ elsif bitrate == 48 then
+ ignore(output.icecast(%vorbis.cbr(bitrate = 48),!source))
elsif bitrate == 64 then
ignore(output.icecast(%vorbis.cbr(bitrate = 64),!source))
elsif bitrate == 96 then
@@ -76,6 +92,14 @@ def output_to(output_type, type, bitrate, host, port, pass, mount_point, url, de
ignore(output.icecast(%vorbis.cbr(bitrate = 128),!source))
elsif bitrate == 160 then
ignore(output.icecast(%vorbis.cbr(bitrate = 160),!source))
+ elsif bitrate == 192 then
+ ignore(output.icecast(%vorbis.cbr(bitrate = 192),!source))
+ elsif bitrate == 224 then
+ ignore(output.icecast(%vorbis.cbr(bitrate = 224),!source))
+ elsif bitrate == 256 then
+ ignore(output.icecast(%vorbis.cbr(bitrate = 256),!source))
+ elsif bitrate == 320 then
+ ignore(output.icecast(%vorbis.cbr(bitrate = 320),!source))
end
end
else
@@ -87,17 +111,29 @@ def output_to(output_type, type, bitrate, host, port, pass, mount_point, url, de
restart_delay = 5,
url = url,
genre = genre)
- if bitrate == 32 then
- ignore(output.shoutcast(%mp3(bitrate = 32),s))
- elsif bitrate == 64 then
- ignore(output.shoutcast(%mp3(bitrate = 64),s))
- elsif bitrate == 96 then
- ignore(output.shoutcast(%mp3(bitrate = 96),s))
- elsif bitrate == 128 then
- ignore(output.shoutcast(%mp3(bitrate = 128),s))
- elsif bitrate == 160 then
- ignore(output.shoutcast(%mp3(bitrate = 160),s))
- end
+ if bitrate == 24 then
+ ignore(output.shoutcast(%mp3(bitrate = 24),s))
+ elsif bitrate == 32 then
+ ignore(output.shoutcast(%mp3(bitrate = 32),s))
+ elsif bitrate == 48 then
+ ignore(output.shoutcast(%mp3(bitrate = 48),s))
+ elsif bitrate == 64 then
+ ignore(output.shoutcast(%mp3(bitrate = 64),s))
+ elsif bitrate == 96 then
+ ignore(output.shoutcast(%mp3(bitrate = 96),s))
+ elsif bitrate == 128 then
+ ignore(output.shoutcast(%mp3(bitrate = 128),s))
+ elsif bitrate == 160 then
+ ignore(output.shoutcast(%mp3(bitrate = 160),s))
+ elsif bitrate == 192 then
+ ignore(output.shoutcast(%mp3(bitrate = 192),s))
+ elsif bitrate == 224 then
+ ignore(output.shoutcast(%mp3(bitrate = 224),s))
+ elsif bitrate == 256 then
+ ignore(output.shoutcast(%mp3(bitrate = 256),s))
+ elsif bitrate == 320 then
+ ignore(output.shoutcast(%mp3(bitrate = 320),s))
+ end
end
end
diff --git a/python_apps/pypo/pypofetch.py b/python_apps/pypo/pypofetch.py
index 1cbbe340f..bc644b2dc 100755
--- a/python_apps/pypo/pypofetch.py
+++ b/python_apps/pypo/pypofetch.py
@@ -103,7 +103,7 @@ class PypoFetch(Thread):
self.schedule_data = m['schedule']
self.process_schedule(self.schedule_data, "scheduler", False)
elif (command == 'update_stream_setting'):
- logger.info("Updating stream setting: %s", m['setting'])
+ logger.info("Updating stream setting...")
self.regenerateLiquidsoapConf(m['setting'])
# ACK the message to take it off the queue
message.ack()
@@ -122,20 +122,58 @@ class PypoFetch(Thread):
key = key.strip()
value = value.strip()
value = value.replace('"', '')
- if value == "dummy_string" or value == "0":
+ if value == "" or value == "0":
value = ''
existing[key] = value
fh.close()
- # flag for any change in cofig
- change = False
+
+ # dict flag for any change in cofig
+ change = {}
+ # this flag is to detect diable -> disable change
+ # in that case, we don't want to restart even if there are chnges.
+ state_change_restart = {}
+ #restart flag
+ restart = False
# look for changes
for s in setting:
- if not s[u'value'] == existing[s[u'keyname']]:
- logger.info("Keyname: %s, Curent value: %s, New Value: %s", s[u'keyname'], s[u'value'], existing[s[u'keyname']])
- change = True
+ if "output_" in s[u'keyname'] and s[u'keyname'] != "output_icecast_vorbis_metadata" and s[u'keyname'] != "output_sound_device":
+ dump, stream = s[u'keyname'].split('_', 1)
+ state_change_restart[stream] = False
+ # This is the case where restart is required no matter what
+ if (existing[s[u'keyname']] != s[u'value']):
+ logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
+ restart = True;
+ # This is the case where we need further checking
+ if s[u'value'] != 'disabled':
+ state_change_restart[stream] = True
+ else:
+ if s[u'keyname'] == "output_sound_device":
+ dump, stream = s[u'keyname'].split('_',1)
+ state_change_restart[stream] = False
+ if not (s[u'value'] == existing[s[u'keyname']]):
+ logger.info("'Need-to-restart' state detected for %s...", s[u'keyname'])
+ state_change_restart[stream] = True
+ elif s[u'keyname'] != "output_icecast_vorbis_metadata" and s[u'keyname'] != "log_file":
+ stream, dump = s[u'keyname'].split('_',1)
+ # setting inital value
+ if stream not in change:
+ change[stream] = False
+ if not (s[u'value'] == existing[s[u'keyname']]):
+ logger.info("Keyname: %s, Curent value: %s, New Value: %s", s[u'keyname'], existing[s[u'keyname']], s[u'value'])
+ change[stream] = True
+
+ # set flag change for sound_device alway True
+ logger.info("Change:%s, State_Change:%s...", change, state_change_restart)
+
+ for k, v in state_change_restart.items():
+ if k == "sound_device" and v:
+ restart = True
+ elif v and change[k]:
+ logger.info("'Need-to-restart' state detected for %s...", k)
+ restart = True
# rewrite
- if change:
+ if restart:
fh = open('/etc/airtime/liquidsoap.cfg', 'w')
logger.info("Rewriting liquidsoap.cfg...")
for d in setting:
@@ -143,7 +181,7 @@ class PypoFetch(Thread):
if(d[u'type'] == 'string'):
temp = d[u'value']
if(temp == ""):
- temp = "dummy_string"
+ temp = ""
buffer += "\"" + temp + "\""
else:
temp = d[u'value']
@@ -153,10 +191,12 @@ class PypoFetch(Thread):
buffer += "\n"
fh.write(buffer)
fh.close()
- # restart playout
- logger.info("Restarting airtime-playout...")
+ # restarting pypo.
+ # we could just restart liquidsoap but it take more time somehow.
+ logger.info("Restarting pypo...")
p = Popen("/etc/init.d/airtime-playout restart >/dev/null 2>&1", shell=True)
sts = os.waitpid(p.pid, 0)[1]
+ self.process_schedule(self.schedule_data, "scheduler", False)
else:
logger.info("No change detected in setting...")
@@ -218,6 +258,8 @@ class PypoFetch(Thread):
# TODO: THIS LIQUIDSOAP STUFF NEEDS TO BE MOVED TO PYPO-PUSH!!!
stream_metadata = schedule_data['stream_metadata']
try:
+ logger.info(LS_HOST)
+ logger.info(LS_PORT)
tn = telnetlib.Telnet(LS_HOST, LS_PORT)
#encode in latin-1 due to telnet protocol not supporting utf-8
tn.write(('vars.stream_metadata_type %s\n' % stream_metadata['format']).encode('latin-1'))
@@ -430,7 +472,7 @@ class PypoFetch(Thread):
# most recent schedule. After that we can just wait for updates.
status, self.schedule_data = self.api_client.get_schedule()
if status == 1:
- self.process_schedule(self.schedule_data , "scheduler", True)
+ self.process_schedule(self.schedule_data , "scheduler", True)
logger.info("Bootstrap complete: got initial copy of the schedule")
loops = 1
diff --git a/python_apps/pypo/pypopush.py b/python_apps/pypo/pypopush.py
index 29f29c088..a9dc79a14 100755
--- a/python_apps/pypo/pypopush.py
+++ b/python_apps/pypo/pypopush.py
@@ -137,9 +137,9 @@ class PypoPush(Thread):
tn.read_all()
except Exception, e:
logger.debug(e)
+ logger.debug('Could not connect to liquidsoap')
self.liquidsoap_state_play = False
- logger.debug('Could not connect to liquidsoap')
def push_liquidsoap(self, pkey, schedule, playlists):