From ea621fd9dc5ba6edd41e40e4218566ed5c26f429 Mon Sep 17 00:00:00 2001 From: denise Date: Wed, 15 Aug 2012 16:40:04 -0400 Subject: [PATCH 1/4] CC-4223: Smart playlists: BPM criteria should have numeric modifiers -done --- airtime_mvc/application/models/Block.php | 8 +++--- airtime_mvc/application/models/StoredFile.php | 2 +- .../models/airtime/map/CcFilesTableMap.php | 2 +- .../models/airtime/om/BaseCcFiles.php | 10 +++---- .../models/airtime/om/BaseCcFilesQuery.php | 27 ++++++++++++------- airtime_mvc/build/schema.xml | 2 +- airtime_mvc/build/sql/schema.sql | 2 +- 7 files changed, 31 insertions(+), 22 deletions(-) diff --git a/airtime_mvc/application/models/Block.php b/airtime_mvc/application/models/Block.php index d8f75fcb0..640389bbe 100644 --- a/airtime_mvc/application/models/Block.php +++ b/airtime_mvc/application/models/Block.php @@ -1148,7 +1148,7 @@ EOT; // e.g 192kps is stored as 192000 $spCriteriaValue = $criteria['value']*1000; } else { - $spCriteriaValue = $criteria['value']; + $spCriteriaValue = addslashes($criteria['value']); } if ($spCriteriaModifier == "starts with") { @@ -1158,15 +1158,15 @@ EOT; } else if ($spCriteriaModifier == "contains" || $spCriteriaModifier == "does not contain") { $spCriteriaValue = "%$spCriteriaValue%"; } else if ($spCriteriaModifier == "is in the range") { - $spCriteriaValue = "$spCriteria > '$spCriteriaValue' AND $spCriteria < '$criteria[extra]'"; + $spCriteriaValue = "$spCriteria > '$spCriteriaValue' AND $spCriteria <= '$criteria[extra]'"; } $spCriteriaModifier = self::$modifier2CriteriaMap[$spCriteriaModifier]; try{ if ($i > 0) { - $qry->addOr($spCriteria, addslashes($spCriteriaValue), $spCriteriaModifier); + $qry->addOr($spCriteria, $spCriteriaValue, $spCriteriaModifier); } else { - $qry->add($spCriteria, addslashes($spCriteriaValue), $spCriteriaModifier); + $qry->add($spCriteria, $spCriteriaValue, $spCriteriaModifier); } }catch (Exception $e){ Logging::log($e); diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index 144cfef73..d0194b54c 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -634,7 +634,7 @@ class Application_Model_StoredFile $streamSelect[] = "EXTRACT(YEAR FROM utime)::varchar AS ".$key; } //need to cast certain data as ints for the union to search on. - else if (in_array($key, array("track_number", "bit_rate", "sample_rate"))) { + else if (in_array($key, array("track_number", "bit_rate", "sample_rate", "bpm"))) { $plSelect[] = "NULL::int AS ".$key; $blSelect[] = "NULL::int AS ".$key; $fileSelect[] = $key; diff --git a/airtime_mvc/application/models/airtime/map/CcFilesTableMap.php b/airtime_mvc/application/models/airtime/map/CcFilesTableMap.php index 99d2ab18d..0f0707722 100644 --- a/airtime_mvc/application/models/airtime/map/CcFilesTableMap.php +++ b/airtime_mvc/application/models/airtime/map/CcFilesTableMap.php @@ -64,7 +64,7 @@ class CcFilesTableMap extends TableMap { $this->addColumn('TRACK_NUMBER', 'DbTrackNumber', 'INTEGER', false, null, null); $this->addColumn('CHANNELS', 'DbChannels', 'INTEGER', false, null, null); $this->addColumn('URL', 'DbUrl', 'VARCHAR', false, 1024, null); - $this->addColumn('BPM', 'DbBpm', 'VARCHAR', false, 8, null); + $this->addColumn('BPM', 'DbBpm', 'INTEGER', false, null, null); $this->addColumn('RATING', 'DbRating', 'VARCHAR', false, 8, null); $this->addColumn('ENCODED_BY', 'DbEncodedBy', 'VARCHAR', false, 255, null); $this->addColumn('DISC_NUMBER', 'DbDiscNumber', 'VARCHAR', false, 8, null); diff --git a/airtime_mvc/application/models/airtime/om/BaseCcFiles.php b/airtime_mvc/application/models/airtime/om/BaseCcFiles.php index 57228452f..3b8daf54f 100644 --- a/airtime_mvc/application/models/airtime/om/BaseCcFiles.php +++ b/airtime_mvc/application/models/airtime/om/BaseCcFiles.php @@ -189,7 +189,7 @@ abstract class BaseCcFiles extends BaseObject implements Persistent /** * The value for the bpm field. - * @var string + * @var int */ protected $bpm; @@ -814,7 +814,7 @@ abstract class BaseCcFiles extends BaseObject implements Persistent /** * Get the [bpm] column value. * - * @return string + * @return int */ public function getDbBpm() { @@ -1822,13 +1822,13 @@ abstract class BaseCcFiles extends BaseObject implements Persistent /** * Set the value of [bpm] column. * - * @param string $v new value + * @param int $v new value * @return CcFiles The current object (for fluent API support) */ public function setDbBpm($v) { if ($v !== null) { - $v = (string) $v; + $v = (int) $v; } if ($this->bpm !== $v) { @@ -2678,7 +2678,7 @@ abstract class BaseCcFiles extends BaseObject implements Persistent $this->track_number = ($row[$startcol + 23] !== null) ? (int) $row[$startcol + 23] : null; $this->channels = ($row[$startcol + 24] !== null) ? (int) $row[$startcol + 24] : null; $this->url = ($row[$startcol + 25] !== null) ? (string) $row[$startcol + 25] : null; - $this->bpm = ($row[$startcol + 26] !== null) ? (string) $row[$startcol + 26] : null; + $this->bpm = ($row[$startcol + 26] !== null) ? (int) $row[$startcol + 26] : null; $this->rating = ($row[$startcol + 27] !== null) ? (string) $row[$startcol + 27] : null; $this->encoded_by = ($row[$startcol + 28] !== null) ? (string) $row[$startcol + 28] : null; $this->disc_number = ($row[$startcol + 29] !== null) ? (string) $row[$startcol + 29] : null; diff --git a/airtime_mvc/application/models/airtime/om/BaseCcFilesQuery.php b/airtime_mvc/application/models/airtime/om/BaseCcFilesQuery.php index 42d2f42f2..1dce43d8d 100644 --- a/airtime_mvc/application/models/airtime/om/BaseCcFilesQuery.php +++ b/airtime_mvc/application/models/airtime/om/BaseCcFilesQuery.php @@ -191,7 +191,7 @@ * @method CcFiles findOneByDbTrackNumber(int $track_number) Return the first CcFiles filtered by the track_number column * @method CcFiles findOneByDbChannels(int $channels) Return the first CcFiles filtered by the channels column * @method CcFiles findOneByDbUrl(string $url) Return the first CcFiles filtered by the url column - * @method CcFiles findOneByDbBpm(string $bpm) Return the first CcFiles filtered by the bpm column + * @method CcFiles findOneByDbBpm(int $bpm) Return the first CcFiles filtered by the bpm column * @method CcFiles findOneByDbRating(string $rating) Return the first CcFiles filtered by the rating column * @method CcFiles findOneByDbEncodedBy(string $encoded_by) Return the first CcFiles filtered by the encoded_by column * @method CcFiles findOneByDbDiscNumber(string $disc_number) Return the first CcFiles filtered by the disc_number column @@ -255,7 +255,7 @@ * @method array findByDbTrackNumber(int $track_number) Return CcFiles objects filtered by the track_number column * @method array findByDbChannels(int $channels) Return CcFiles objects filtered by the channels column * @method array findByDbUrl(string $url) Return CcFiles objects filtered by the url column - * @method array findByDbBpm(string $bpm) Return CcFiles objects filtered by the bpm column + * @method array findByDbBpm(int $bpm) Return CcFiles objects filtered by the bpm column * @method array findByDbRating(string $rating) Return CcFiles objects filtered by the rating column * @method array findByDbEncodedBy(string $encoded_by) Return CcFiles objects filtered by the encoded_by column * @method array findByDbDiscNumber(string $disc_number) Return CcFiles objects filtered by the disc_number column @@ -1061,20 +1061,29 @@ abstract class BaseCcFilesQuery extends ModelCriteria /** * Filter the query on the bpm column * - * @param string $dbBpm The value to use as filter. - * Accepts wildcards (* and % trigger a LIKE) + * @param int|array $dbBpm The value to use as filter. + * Accepts an associative array('min' => $minValue, 'max' => $maxValue) * @param string $comparison Operator to use for the column comparison, defaults to Criteria::EQUAL * * @return CcFilesQuery The current query, for fluid interface */ public function filterByDbBpm($dbBpm = null, $comparison = null) { - if (null === $comparison) { - if (is_array($dbBpm)) { + if (is_array($dbBpm)) { + $useMinMax = false; + if (isset($dbBpm['min'])) { + $this->addUsingAlias(CcFilesPeer::BPM, $dbBpm['min'], Criteria::GREATER_EQUAL); + $useMinMax = true; + } + if (isset($dbBpm['max'])) { + $this->addUsingAlias(CcFilesPeer::BPM, $dbBpm['max'], Criteria::LESS_EQUAL); + $useMinMax = true; + } + if ($useMinMax) { + return $this; + } + if (null === $comparison) { $comparison = Criteria::IN; - } elseif (preg_match('/[\%\*]/', $dbBpm)) { - $dbBpm = str_replace('*', '%', $dbBpm); - $comparison = Criteria::LIKE; } } return $this->addUsingAlias(CcFilesPeer::BPM, $dbBpm, $comparison); diff --git a/airtime_mvc/build/schema.xml b/airtime_mvc/build/schema.xml index 6486208ed..6a81f5ae4 100644 --- a/airtime_mvc/build/schema.xml +++ b/airtime_mvc/build/schema.xml @@ -38,7 +38,7 @@ - + diff --git a/airtime_mvc/build/sql/schema.sql b/airtime_mvc/build/sql/schema.sql index 421b4cff4..2fb1bd978 100644 --- a/airtime_mvc/build/sql/schema.sql +++ b/airtime_mvc/build/sql/schema.sql @@ -56,7 +56,7 @@ CREATE TABLE "cc_files" "track_number" INTEGER, "channels" INTEGER, "url" VARCHAR(1024), - "bpm" VARCHAR(8), + "bpm" INTEGER, "rating" VARCHAR(8), "encoded_by" VARCHAR(255), "disc_number" VARCHAR(8), From 1e57c12ce7e1fabb5c0893f4835c770c9efb5e29 Mon Sep 17 00:00:00 2001 From: Martin Konecny Date: Wed, 15 Aug 2012 16:43:01 -0400 Subject: [PATCH 2/4] CC-1665: Scheduled stream rebroadcasting and recording -pre buffer streams part 1 --- airtime_mvc/application/models/Schedule.php | 18 ++++++- .../pypo/liquidsoap_scripts/ls_lib.liq | 6 +-- .../pypo/liquidsoap_scripts/ls_script.liq | 50 +++++++++++++++---- python_apps/pypo/pypopush.py | 32 +++++++++--- 4 files changed, 85 insertions(+), 21 deletions(-) diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index 863417751..07de1f4b8 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -698,6 +698,22 @@ SQL; ); if ($type == "stream") { + //create an event to start stream buffering 5 seconds ahead of the streams actual time. + $buffer_start = new DateTime($item["start"], new DateTimeZone('UTC')); + $buffer_start->sub(new DateInterval("PT5S")); + + $stream_buffer_start = Application_Model_Schedule::AirtimeTimeToPypoTime($buffer_start->format("Y-m-d H:i:s")); + + //TODO: Make sure no other media is being overwritten! + $data["media"][$stream_buffer_start] = array( + 'start' => $stream_buffer_start, + 'end' => $stream_buffer_start, + 'uri' => $uri, + 'type' => 'stream_buffer_start', + 'independent_event' => true + ); + + //since a stream never ends we have to insert an additional "kick stream" event. The "start" //time of this event is the "end" time of the stream minus 1 second. $dt = new DateTime($item["end"], new DateTimeZone('UTC')); @@ -920,7 +936,7 @@ SQL; $hValue = trim(substr($data["add_show_duration"], 0, $hPos)); } if ($mPos !== false) { - $hPos = $hPos === FALSE ? 0 : $hPos+1; + $hPos = $hPos === false ? 0 : $hPos+1; $mValue = trim(substr($data["add_show_duration"], $hPos, -1 )); } diff --git a/python_apps/pypo/liquidsoap_scripts/ls_lib.liq b/python_apps/pypo/liquidsoap_scripts/ls_lib.liq index ed5085e12..feb303e68 100644 --- a/python_apps/pypo/liquidsoap_scripts/ls_lib.liq +++ b/python_apps/pypo/liquidsoap_scripts/ls_lib.liq @@ -1,5 +1,5 @@ def notify(m) - current_media_id := string_of(m['schedule_table_id']) + #current_media_id := string_of(m['schedule_table_id']) command = "/usr/lib/airtime/pypo/bin/liquidsoap_scripts/notify.sh --data='#{!pypo_data}' --media-id=#{m['schedule_table_id']} &" log(command) system(command) @@ -7,7 +7,7 @@ end def notify_stream(m) json_str = string.replace(pattern="\n",(fun (s) -> ""), json_of(m)) - command = "/usr/lib/airtime/pypo/bin/liquidsoap_scripts/notify.sh --webstream='#{json_str}' --media-id=#{!current_media_id} &" + command = "/usr/lib/airtime/pypo/bin/liquidsoap_scripts/notify.sh --webstream='#{json_str}' --media-id=#{!current_dyn_id} &" log(command) system(command) end @@ -253,7 +253,7 @@ end # Function to create a playlist source and output it. def create_dynamic_source(uri) = # The playlist source - s = input.http(uri) + s = input.http(buffer=4., max=12., uri) # The output active_dyn_out = dyn_out(s) diff --git a/python_apps/pypo/liquidsoap_scripts/ls_script.liq b/python_apps/pypo/liquidsoap_scripts/ls_script.liq index 540bd29fa..7d3c1d56e 100644 --- a/python_apps/pypo/liquidsoap_scripts/ls_script.liq +++ b/python_apps/pypo/liquidsoap_scripts/ls_script.liq @@ -8,6 +8,7 @@ set("server.telnet.port", 1234) #Dynamic source list dyn_sources = ref [] +webstream_enabled = ref false time = ref string_of(gettimeofday()) @@ -27,7 +28,7 @@ stream_metadata_type = ref 0 default_dj_fade = ref 0. station_name = ref '' show_name = ref '' -current_media_id = ref '' +#current_media_id = ref '' s1_connected = ref '' s2_connected = ref '' @@ -47,16 +48,43 @@ queue = map_metadata(append_title, queue) # the crossfade function controls fade in/out queue = crossfade(queue) -queue = fallback([web_stream, queue]) +queue = switch(track_sensitive=false, [({!webstream_enabled},web_stream), ({true}, queue)]) + ignore(output.dummy(queue, fallible=true)) -server.register(namespace="vars", "pypo_data", fun (s) -> begin pypo_data := s "Done" end) -server.register(namespace="vars", "stream_metadata_type", fun (s) -> begin stream_metadata_type := int_of_string(s) s end) -server.register(namespace="vars", "show_name", fun (s) -> begin show_name := s s end) -server.register(namespace="vars", "station_name", fun (s) -> begin station_name := s s end) -server.register(namespace="vars", "bootup_time", fun (s) -> begin time := s s end) -server.register(namespace="streams", "connection_status", fun (s) -> begin "1:#{!s1_connected},2:#{!s2_connected},3:#{!s3_connected}" end) -server.register(namespace="vars", "default_dj_fade", fun (s) -> begin default_dj_fade := float_of_string(s) s end) +server.register(namespace="vars", + "pypo_data", + fun (s) -> begin pypo_data := s "Done" end) +server.register(namespace="vars", + "stream_metadata_type", + fun (s) -> begin stream_metadata_type := int_of_string(s) s end) +server.register(namespace="vars", + "show_name", + fun (s) -> begin show_name := s s end) +server.register(namespace="vars", + "station_name", + fun (s) -> begin station_name := s s end) +server.register(namespace="vars", + "bootup_time", + fun (s) -> begin time := s s end) +server.register(namespace="streams", + "connection_status", + fun (s) -> begin "1:#{!s1_connected},2:#{!s2_connected},3:#{!s3_connected}" end) +server.register(namespace="vars", + "default_dj_fade", + fun (s) -> begin default_dj_fade := float_of_string(s) s end) + +server.register(namespace="dynamic_source", + description="Enable webstream output", + usage='start', + "output_start", + fun (s) -> begin webstream_enabled := true "enabled" end) +server.register(namespace="dynamic_source", + description="Enable webstream output", + usage='stop', + "output_stop", + fun (s) -> begin webstream_enabled := false "disabled" end) + server.register(namespace="dynamic_source", description="Set the cc_schedule row id", usage="id ", @@ -65,12 +93,12 @@ server.register(namespace="dynamic_source", server.register(namespace="dynamic_source", description="Start a new dynamic source.", usage="start ", - "start", + "read_start", create_dynamic_source) server.register(namespace="dynamic_source", description="Stop a dynamic source.", usage="stop ", - "stop", + "read_stop", destroy_dynamic_source) default = amplify(id="silence_src", 0.00001, noise()) diff --git a/python_apps/pypo/pypopush.py b/python_apps/pypo/pypopush.py index 09dd7de37..09c1c89a1 100644 --- a/python_apps/pypo/pypopush.py +++ b/python_apps/pypo/pypopush.py @@ -386,17 +386,33 @@ class PypoPush(Thread): PypoFetch.disconnect_source(self.logger, self.telnet_lock, "live_dj") elif media_item['event_type'] == "switch_off": PypoFetch.switch_source(self.logger, self.telnet_lock, "live_dj", "off") + elif media_item['type'] == 'stream_buffer_start': + self.start_web_stream_buffer(media_item) elif media_item['type'] == "stream": - """ - Source is a stream that we need to being downloading to a file. Then we may simply - point Liquidsoap to play this file. - """ self.start_web_stream(media_item) elif media_item['type'] == "stream_end": self.stop_web_stream(media_item) except Exception, e: self.logger.error('Pypo Push Exception: %s', e) + def start_web_stream_buffer(self, media_item): + try: + self.telnet_lock.acquire() + tn = telnetlib.Telnet(LS_HOST, LS_PORT) + + msg = 'dynamic_source.read_start\n' + self.logger.debug(msg) + tn.write(msg) + + tn.write("exit\n") + self.logger.debug(tn.read_all()) + except Exception, e: + self.logger.error(str(e)) + finally: + self.telnet_lock.release() + + + def start_web_stream(self, media_item): try: self.telnet_lock.acquire() @@ -410,7 +426,7 @@ class PypoPush(Thread): msg = 'streams.scheduled_play_start\n' tn.write(msg) - msg = 'dynamic_source.start %s\n' % media_item['uri'].encode('latin-1') + msg = 'dynamic_source.output_start\n' self.logger.debug(msg) tn.write(msg) @@ -429,7 +445,11 @@ class PypoPush(Thread): tn = telnetlib.Telnet(LS_HOST, LS_PORT) #dynamic_source.stop http://87.230.101.24:80/top100station.mp3 - msg = 'dynamic_source.stop %s\n' % media_item['uri'].encode('latin-1') + msg = 'dynamic_source.read_stop %s\n' % media_item['uri'].encode('latin-1') + self.logger.debug(msg) + tn.write(msg) + + msg = 'dynamic_source.output_stop\n' self.logger.debug(msg) tn.write(msg) From 4ee0c368ac14e1566da31388438aa3f09bb06fe8 Mon Sep 17 00:00:00 2001 From: Martin Konecny Date: Wed, 15 Aug 2012 17:03:14 -0400 Subject: [PATCH 3/4] CC-1665: Scheduled stream rebroadcasting and recording -pre buffer streams part 2 --- airtime_mvc/application/models/Schedule.php | 1 + python_apps/pypo/pypopush.py | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/airtime_mvc/application/models/Schedule.php b/airtime_mvc/application/models/Schedule.php index 07de1f4b8..f7f518f4c 100644 --- a/airtime_mvc/application/models/Schedule.php +++ b/airtime_mvc/application/models/Schedule.php @@ -709,6 +709,7 @@ SQL; 'start' => $stream_buffer_start, 'end' => $stream_buffer_start, 'uri' => $uri, + 'row_id' => $item["id"], 'type' => 'stream_buffer_start', 'independent_event' => true ); diff --git a/python_apps/pypo/pypopush.py b/python_apps/pypo/pypopush.py index 09c1c89a1..b04c60d82 100644 --- a/python_apps/pypo/pypopush.py +++ b/python_apps/pypo/pypopush.py @@ -400,7 +400,11 @@ class PypoPush(Thread): self.telnet_lock.acquire() tn = telnetlib.Telnet(LS_HOST, LS_PORT) - msg = 'dynamic_source.read_start\n' + msg = 'dynamic_source.id %s\n' % media_item['row_id'] + tn.write(msg) + + #example: dynamic_source.read_start http://87.230.101.24:80/top100station.mp3 + msg = 'dynamic_source.read_start %s\n' % media_item['uri'].encode('latin-1') self.logger.debug(msg) tn.write(msg) @@ -412,15 +416,10 @@ class PypoPush(Thread): self.telnet_lock.release() - def start_web_stream(self, media_item): try: self.telnet_lock.acquire() tn = telnetlib.Telnet(LS_HOST, LS_PORT) - #dynamic_source.start http://87.230.101.24:80/top100station.mp3 - - msg = 'dynamic_source.id %s\n' % media_item['row_id'] - tn.write(msg) #TODO: DO we need this? msg = 'streams.scheduled_play_start\n' From 5c1c836f11d1b3420d3fb2e9adac6cc7550eee6a Mon Sep 17 00:00:00 2001 From: Martin Konecny Date: Wed, 15 Aug 2012 18:30:12 -0400 Subject: [PATCH 4/4] CC-1665: Scheduled stream rebroadcasting and recording -pre buffer streams part 3 -finished --- python_apps/pypo/liquidsoap_scripts/ls_lib.liq | 2 +- python_apps/pypo/liquidsoap_scripts/ls_script.liq | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/python_apps/pypo/liquidsoap_scripts/ls_lib.liq b/python_apps/pypo/liquidsoap_scripts/ls_lib.liq index feb303e68..172ba77b1 100644 --- a/python_apps/pypo/liquidsoap_scripts/ls_lib.liq +++ b/python_apps/pypo/liquidsoap_scripts/ls_lib.liq @@ -253,7 +253,7 @@ end # Function to create a playlist source and output it. def create_dynamic_source(uri) = # The playlist source - s = input.http(buffer=4., max=12., uri) + s = input.http(buffer=2., max=12., uri) # The output active_dyn_out = dyn_out(s) diff --git a/python_apps/pypo/liquidsoap_scripts/ls_script.liq b/python_apps/pypo/liquidsoap_scripts/ls_script.liq index 7d3c1d56e..ddc7ae1a1 100644 --- a/python_apps/pypo/liquidsoap_scripts/ls_script.liq +++ b/python_apps/pypo/liquidsoap_scripts/ls_script.liq @@ -11,7 +11,6 @@ dyn_sources = ref [] webstream_enabled = ref false time = ref string_of(gettimeofday()) - 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) @@ -40,7 +39,7 @@ just_switched = ref false %include "ls_lib.liq" -web_stream = input.harbor("test-harbor",port=8999,password="hackme") +web_stream = mksafe(input.harbor("test-harbor", port=8999, password="hackme")) web_stream = on_metadata(notify_stream, web_stream) queue = on_metadata(notify, queue) @@ -48,7 +47,7 @@ queue = map_metadata(append_title, queue) # the crossfade function controls fade in/out queue = crossfade(queue) -queue = switch(track_sensitive=false, [({!webstream_enabled},web_stream), ({true}, queue)]) +queue = switch(id="stream_queue_switch", track_sensitive=false, [({!webstream_enabled},web_stream), ({true}, queue)]) ignore(output.dummy(queue, fallible=true))