From 8249ea881645d93abff15ec39227e7ab0609b104 Mon Sep 17 00:00:00 2001 From: Martin Konecny Date: Tue, 28 Aug 2012 12:38:13 -0400 Subject: [PATCH 1/3] CC-1665: Scheduled stream rebroadcasting and recording -small optimization for user auth --- airtime_mvc/application/controllers/WebstreamController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/airtime_mvc/application/controllers/WebstreamController.php b/airtime_mvc/application/controllers/WebstreamController.php index 4f6cf4053..894171c17 100644 --- a/airtime_mvc/application/controllers/WebstreamController.php +++ b/airtime_mvc/application/controllers/WebstreamController.php @@ -102,7 +102,7 @@ class WebstreamController extends Zend_Controller_Action $hasPermission = true; } - if ($user->isHost()) { + if (!$hasPermission && $user->isHost()) { if ($webstream_id != -1) { $webstream = CcWebstreamQuery::create()->findPK($webstream_id); //we are updating a playlist. Ensure that if the user is a host/dj, that he has the correct permission. From 8cf048d459ec1d97b3e757df9f0fd49d363cccad Mon Sep 17 00:00:00 2001 From: Martin Konecny Date: Tue, 28 Aug 2012 13:14:13 -0400 Subject: [PATCH 2/3] CC-1665: Scheduled stream rebroadcasting and recording -parse m3u playlists to get actual stream --- airtime_mvc/application/models/Webstream.php | 41 +++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/airtime_mvc/application/models/Webstream.php b/airtime_mvc/application/models/Webstream.php index 96dee8f99..b66967f95 100644 --- a/airtime_mvc/application/models/Webstream.php +++ b/airtime_mvc/application/models/Webstream.php @@ -170,7 +170,7 @@ class Application_Model_Webstream implements Application_Model_LibraryEditable //TODO: return url $mediaUrl = self::getMediaUrl($url, $mime, $content_length_found); - if (preg_match("/xspf\+xml/", $mime)) { + if (preg_match("/(x-mpegurl)|(xspf\+xml)/", $mime)) { list($mime, $content_length_found) = self::discoverStreamMime($mediaUrl); } } catch (Exception $e) { @@ -244,17 +244,46 @@ class Application_Model_Webstream implements Application_Model_LibraryEditable throw new Exception("Could not parse XSPF playlist"); } + private static function getM3uUrl($url) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + + // grab URL and pass it to the browser + //TODO: What if invalid url? + $content = curl_exec($ch); + Logging::info($content); + curl_close($ch); + + //split into lines: + $delim = "\n"; + if (strpos($content, "\r\n") !== false) { + $delim = "\r\n"; + } + $lines = explode("$delim", $content); + #$lines = preg_split('/$\R?^/m', $content); + + if (count($lines) > 0) { + return $lines[0]; + } + + throw new Exception("Could not parse M3U playlist"); + } + private static function getMediaUrl($url, $mime, $content_length_found) { - if (preg_match("/(mpeg|ogg)/", $mime)) { + + if (preg_match("/x-mpegurl/", $mime)) { + $media_url = self::getM3uUrl($url); + } else if (preg_match("/xspf\+xml/", $mime)) { + $media_url = self::getXspfUrl($url); + } else if (preg_match("/(mpeg|ogg)/", $mime)) { if ($content_length_found) { throw new Exception("Invalid webstream - This appears to be a file download."); } $media_url = $url; - } else if (preg_match("/x-mpegurl/", $mime)) { - $media_url = $url; - } else if (preg_match("/xspf\+xml/", $mime)) { - $media_url = self::getXspfUrl($url); } else { throw new Exception("Unrecognized stream type: $mime"); } From 8d81ecf3ad21613fe0a511242bf9e71550e3cad5 Mon Sep 17 00:00:00 2001 From: Martin Konecny Date: Tue, 28 Aug 2012 15:00:02 -0400 Subject: [PATCH 3/3] CC-4114: separate Liquidsoap into its own init.d script if liquidsoap restarts, automatically notify pypo --- python_apps/api_clients/api_client.cfg | 3 +++ python_apps/api_clients/api_client.py | 16 ++++++++++++++++ python_apps/pypo/airtime-liquidsoap-init-d | 12 ++++++++++++ python_apps/pypo/airtime-playout | 12 ++++++++++++ .../pypo/liquidsoap_scripts/ls_script.liq | 4 +++- python_apps/pypo/pyponotify.py | 14 +++++++++++++- 6 files changed, 59 insertions(+), 2 deletions(-) diff --git a/python_apps/api_clients/api_client.cfg b/python_apps/api_clients/api_client.cfg index 2e0e938e8..4a4c351b6 100644 --- a/python_apps/api_clients/api_client.cfg +++ b/python_apps/api_clients/api_client.cfg @@ -117,3 +117,6 @@ get_files_without_replay_gain = 'get-files-without-replay-gain/api_key/%%api_key update_replay_gain_value = 'update-replay-gain-value/api_key/%%api_key%%' notify_webstream_data = 'notify-webstream-data/api_key/%%api_key%%/media_id/%%media_id%%/format/json' + +notify_liquidsoap_started = 'rabbitmq-do-push/api_key/%%api_key%%/format/json' + diff --git a/python_apps/api_clients/api_client.py b/python_apps/api_clients/api_client.py index faf772a9a..25862281a 100644 --- a/python_apps/api_clients/api_client.py +++ b/python_apps/api_clients/api_client.py @@ -229,6 +229,22 @@ class AirtimeApiClient(): except Exception, e: logger.error("%s", e) + def notify_liquidsoap_started(self): + logger = self.logger + + try: + url = "http://%s:%s/%s/%s" % (self.config["base_url"], \ + str(self.config["base_port"]), \ + self.config["api_base"], \ + self.config["notify_liquidsoap_started"]) + + url = url.replace("%%api_key%%", self.config["api_key"]) + + self.get_response_from_server(url) + except Exception, e: + logger.error("Exception: %s", str(e)) + + """ This is a callback from liquidsoap, we use this to notify about the currently playing *song*. We get passed a JSON string which we handed to diff --git a/python_apps/pypo/airtime-liquidsoap-init-d b/python_apps/pypo/airtime-liquidsoap-init-d index 26ac2d104..4de363c41 100755 --- a/python_apps/pypo/airtime-liquidsoap-init-d +++ b/python_apps/pypo/airtime-liquidsoap-init-d @@ -55,6 +55,18 @@ case "${1:-''}" in start echo "Done." ;; + + 'status') + if [ -f "/var/run/airtime-liquidsoap.pid" ]; then + pid=`cat /var/run/airtime-liquidsoap.pid` + if [ -d "/proc/$pid" ]; then + echo "Liquidsoap is running" + exit 0 + fi + fi + echo "Liquidsoap is not running" + exit 1 + ;; 'start-no-monit') # restart commands here echo -n "Starting $NAME: " diff --git a/python_apps/pypo/airtime-playout b/python_apps/pypo/airtime-playout index 15074106b..916ba5da5 100755 --- a/python_apps/pypo/airtime-playout +++ b/python_apps/pypo/airtime-playout @@ -25,6 +25,18 @@ export PYTHONPATH=${api_client_path}:$PYTHONPATH export LC_ALL=`cat /etc/default/locale | grep "LANG=" | cut -d= -f2 | tr -d "\n\""` export TERM=xterm + +#If Pypo is starting from scratch let's restart Liquidsoap as well. This is so +#that the Liquidsoap queue #is emptied and we can start from a known state. +/etc/init.d/airtime-liquidsoap status +status="$?" + +if [ "$status" == "0" ]; then + /etc/init.d/airtime-liquidsoap restart +else + /etc/init.d/airtime-liquidsoap start +fi + exec python ${pypo_path}${pypo_script} > /var/log/airtime/pypo/py-interpreter.log 2>&1 # EOF diff --git a/python_apps/pypo/liquidsoap_scripts/ls_script.liq b/python_apps/pypo/liquidsoap_scripts/ls_script.liq index 3696b0d83..bc5bed633 100644 --- a/python_apps/pypo/liquidsoap_scripts/ls_script.liq +++ b/python_apps/pypo/liquidsoap_scripts/ls_script.liq @@ -323,4 +323,6 @@ if s3_enable == true then output_to(s3_output, s3_type, s3_bitrate, s3_host, s3_port, s3_pass, s3_mount, s3_url, s3_description, s3_genre, s3_user, s, "3", s3_connected, s3_description) end -#ignore(output.dummy(blank())) +command = "/usr/lib/airtime/pypo/bin/liquidsoap_scripts/notify.sh --liquidsoap-started &" +log(command) +system(command) diff --git a/python_apps/pypo/pyponotify.py b/python_apps/pypo/pyponotify.py index 7b1d5ffd2..bd401fdf3 100644 --- a/python_apps/pypo/pyponotify.py +++ b/python_apps/pypo/pyponotify.py @@ -42,6 +42,8 @@ parser.add_option("-t", "--time", help="Liquidsoap boot up time", action="store" parser.add_option("-x", "--source-name", help="source connection name", metavar="source_name") parser.add_option("-y", "--source-status", help="source connection status", metavar="source_status") parser.add_option("-w", "--webstream", help="JSON metadata associated with webstream", metavar="json_data") +parser.add_option("-n", "--liquidsoap-started", help="notify liquidsoap started", metavar="json_data", action="store_true", default=True) + # parse options (options, args) = parser.parse_args() @@ -67,6 +69,11 @@ class Notify: def __init__(self): self.api_client = api_client.AirtimeApiClient() + def notify_liquidsoap_started(self): + logger = logging.getLogger("notify") + logger.debug("Notifying server that Liquidsoap has started") + self.api_client.notify_liquidsoap_started() + def notify_media_start_playing(self, data, media_id): logger = logging.getLogger("notify") @@ -105,7 +112,6 @@ class Notify: logger.debug('#################################################') response = self.api_client.notify_webstream_data(data, media_id) - pass if __name__ == '__main__': print @@ -140,6 +146,12 @@ if __name__ == '__main__': n.notify_webstream_data(options.webstream, options.media_id) except Exception, e: print e + elif options.liquidsoap_started: + try: + n = Notify() + n.notify_liquidsoap_started() + except Exception, e: + print e else: if not options.data: print "NOTICE: 'data' command-line argument not given."