From e03c167f935492f7dad48a15b35af6d92974cd8b Mon Sep 17 00:00:00 2001 From: David Trattnig <david.trattnig@o94.at> Date: Thu, 15 Oct 2020 19:28:09 +0200 Subject: [PATCH] A/B scheduled fallback queue. #43 --- modules/liquidsoap/engine.liq | 64 ++++++----- modules/liquidsoap/in_fallback.liq | 118 +++++++++++++++++++++ modules/liquidsoap/in_filesystem.liq | 152 --------------------------- modules/liquidsoap/in_queue.liq | 105 ++++++++++++++++++ modules/liquidsoap/library.liq | 38 +++++++ modules/liquidsoap/settings.liq | 7 ++ 6 files changed, 303 insertions(+), 181 deletions(-) create mode 100644 modules/liquidsoap/in_fallback.liq delete mode 100644 modules/liquidsoap/in_filesystem.liq create mode 100644 modules/liquidsoap/in_queue.liq diff --git a/modules/liquidsoap/engine.liq b/modules/liquidsoap/engine.liq index d3d7efed..46a783cb 100644 --- a/modules/liquidsoap/engine.liq +++ b/modules/liquidsoap/engine.liq @@ -26,23 +26,30 @@ inputs = ref [] # Load settings from ini file %include "settings.liq" -print("**************************************************************************************") -print(" ENGINE LIQUIDSOAP CONFIGURATION ") -print("**************************************************************************************") -print(" Engine Configuration Folder: #{engine_config_folder}") -print(" Station Fallback Playlist: #{fallback_station_playlist_path}") -print(" Station Fallback Directory: #{fallback_station_dir}") -print("**************************************************************************************") - # Include some functions %include "library.liq" -###################### -# INPUTS # -###################### +##################################### +# EVENTS # +##################################### + +# Called when some new metadata info is available +def on_metadata_notification(meta) = + filename = meta["filename"] + # artist = meta["artist"] + # title = meta["title"] + system('#{list.assoc(default="", "install_dir", ini)}/guru.py --on_play "#{filename}"') +end + +##################################### +# INPUTS # +##################################### + +# Enable queue sources +%include "in_queue.liq" -# Enable play from filesystem -%include "in_filesystem.liq" +# Enable fallback sources +%include "in_fallback.liq" # Enable stream overtakes %include "in_stream.liq" @@ -50,10 +57,10 @@ print("************************************************************************* # Enabled line in from soundcard %include "in_soundcard.liq" -###################### -# ROUTING # -###################### +##################################### +# ROUTING # +##################################### mixer = mix(id="mixer", list.append( @@ -70,7 +77,7 @@ mixer = mix(id="mixer", ) stripped_stream = strip_blank( - id='strip_blank', + id="strip_blank", track_sensitive=false, max_blank=fallback_max_blank, min_noise=fallback_min_noise, @@ -78,39 +85,38 @@ stripped_stream = strip_blank( mixer ) + # When some regular playout is happening and it is returned to the fallback, # we don't want to resume the previous fallback track: def on_track_change(s) = source.skip(station_playlist) source.skip(station_folder) end - stripped_stream = on_track(on_track_change, stripped_stream) - -output_source = fallback( +fallback_one = fallback( id="fallback-scheduled", track_sensitive=false, replay_metadata=true, - [ stripped_stream, fallback_playlist]) + [ stripped_stream, input_fallback_scheduled_0, input_fallback_scheduled_1]) -output_source = fallback( +fallback_two = fallback( id="fallback-station-playlist", track_sensitive=false, replay_metadata=true, - [ output_source, station_playlist]) + [ fallback_one, station_playlist]) -output_source = fallback( +fallback_three = fallback( id="fallback-station-folder", track_sensitive=false, replay_metadata=true, - [ output_source, station_folder]) - + [ fallback_two, station_folder]) +output_source = fallback_three -###################### -# OUTPUTS # -###################### +##################################### +# OUTPUTS # +##################################### # create soundcard output %include "out_soundcard.liq" diff --git a/modules/liquidsoap/in_fallback.liq b/modules/liquidsoap/in_fallback.liq new file mode 100644 index 00000000..327b9b9b --- /dev/null +++ b/modules/liquidsoap/in_fallback.liq @@ -0,0 +1,118 @@ +# +# Aura Engine (https://gitlab.servus.at/aura/engine) +# +# Copyright (C) 2017-2020 - The Aura Engine Team. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + + + +##################################### +# FALLBACK SOURCES # +##################################### + + +# Create Sources +input_fallback_scheduled_0 = request.equeue(id="in_fallback_scheduled_0") +input_fallback_scheduled_1 = request.equeue(id="in_fallback_scheduled_1") + +station_playlist = playlist( + id="station_playlist", + fallback_station_playlist_path, + mode="randomize", + reload_mode="watch", + reload=0) + +station_folder = mksafe( + playlist(id="station_folder", + fallback_station_dir, + mode="randomize", + reload=fallback_station_dir_reload, + reload_mode="seconds")) + +# Apply ReplayGain Normalization +input_fallback_scheduled_0 = amplify(id="in_fallback_scheduled_0", 1., override="replay_gain", input_fallback_scheduled_0) +input_fallback_scheduled_1 = amplify(id="in_fallback_scheduled_1", 1., override="replay_gain", input_fallback_scheduled_1) +station_playlist = amplify(id="station_playlist", 1., override="replay_gain", station_playlist) +station_folder = amplify(id="station_folder", 1., override="replay_gain", station_folder) + +# Add Event Handlers +input_fallback_scheduled_0 = on_metadata(id="in_fallback_scheduled_0", on_metadata_notification, input_fallback_scheduled_0) +input_fallback_scheduled_1 = on_metadata(id="in_fallback_scheduled_1", on_metadata_notification, input_fallback_scheduled_1) +station_playlist = on_metadata(id="station_playlist", on_metadata_notification, station_playlist) +station_folder = on_metadata(id="station_folder", on_metadata_notification, station_folder) + + +##################################### +# SERVER FUNCTIONS # +##################################### + + + +# Clear Fallback Queue A +server.register(namespace=source.id(input_fallback_scheduled_0), + description="Clear all items of the scheduled fallback queue A.", + usage="clear", + "clear", + + fun (s) -> + begin + clear_queue(input_fallback_scheduled_0) + "Clearing done." + end + ) + +# Clear Fallback Queue B +server.register(namespace=source.id(input_fallback_scheduled_1), + description="Clear all items of the scheduled fallback queue B.", + usage="clear", + "clear", + + fun (s) -> + begin + clear_queue(input_fallback_scheduled_1) + "Clearing done." + end + ) + +# Seek Fallback Queue A +server.register(namespace = source.id(input_fallback_scheduled_0), + description="Seek to relative position in #{source.id(input_fallback_scheduled_0)}", + usage = "seek <duration in seconds>", + "seek", + + fun (t) -> + begin + log("Seeking #{t} sec") + t = float_of_string(default=0.,t) + ret = source.seek(input_fallback_scheduled_0, t) + "Seeked #{ret} seconds." + end + ) + +# Seek Fallback Queue B +server.register(namespace = source.id(input_fallback_scheduled_1), + description="Seek to relative position in #{source.id(input_fallback_scheduled_1)}", + usage = "seek <duration in seconds>", + "seek", + + fun (t) -> + begin + log("Seeking #{t} sec") + t = float_of_string(default=0.,t) + ret = source.seek(input_fallback_scheduled_1, t) + "Seeked #{ret} seconds." + end + ) \ No newline at end of file diff --git a/modules/liquidsoap/in_filesystem.liq b/modules/liquidsoap/in_filesystem.liq deleted file mode 100644 index fae0b2e0..00000000 --- a/modules/liquidsoap/in_filesystem.liq +++ /dev/null @@ -1,152 +0,0 @@ -# -# Aura Engine (https://gitlab.servus.at/aura/engine) -# -# Copyright (C) 2017-2020 - The Aura Engine Team. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - - -# Called when some new metadata info is available -def on_metadata_notification(meta) = - filename = meta["filename"] - # artist = meta["artist"] - # title = meta["title"] - system('#{list.assoc(default="", "install_dir", ini)}/guru.py --on_play "#{filename}"') -end - - -##################################### -# DEFAULT SOURCES # -##################################### - -# Create Filesystem Sources -input_filesystem_0 = request.equeue(id="in_filesystem_0") -input_filesystem_1 = request.equeue(id="in_filesystem_1") - -# Apply ReplayGain Normalization -input_filesystem_0 = amplify(id="in_filesystem_0", 1., override="replay_gain", input_filesystem_0) -input_filesystem_1 = amplify(id="in_filesystem_1", 1., override="replay_gain", input_filesystem_1) - -# Add Event Handlers -input_filesystem_0 = on_metadata(id="in_filesystem_0", on_metadata_notification, input_filesystem_0) -input_filesystem_1 = on_metadata(id="in_filesystem_1", on_metadata_notification, input_filesystem_1) - - -##################################### -# FALLBACK SOURCES # -##################################### - -fallback_playlist = playlist( - id="playlist_fallback_scheduled", - "", - mode="normal", - reload_mode="watch") - -station_playlist = playlist( - id="station_playlist", - fallback_station_playlist_path, - mode="randomize", - reload_mode="watch", - reload=10) - -station_folder = mksafe( - playlist(id="station_folder", - fallback_station_dir, - mode="randomize", - reload=fallback_station_dir_reload, - reload_mode="seconds")) - -# Apply ReplayGain Normalization -fallback_playlist = amplify(id="playlist_fallback_scheduled", 1., override="replay_gain", fallback_playlist) -station_playlist = amplify(id="station_playlist", 1., override="replay_gain", station_playlist) -station_folder = amplify(id="station_folder", 1., override="replay_gain", station_folder) - -# Add Event Handlers -fallback_playlist = on_metadata(id="playlist_fallback_scheduled", on_metadata_notification, fallback_playlist) -station_playlist = on_metadata(id="station_playlist", on_metadata_notification, station_playlist) -station_folder = on_metadata(id="station_folder", on_metadata_notification, station_folder) - - -##################################### -# SERVER FUNCTIONS # -##################################### - -def clear_items(ns) = - ret = server.execute("#{source.id(ns)}.primary_queue") - ret = list.hd(default="", ret) - if ret == "" then - log("Queue cleared.") - (-1.) - else - log("There are still items in the queue, trying skip ...") - source.skip(ns) - (0.1) - end -end - -def clear_queue(ns) = - add_timeout(fast=false, 0.5, {clear_items(ns)}) -end - -# Clear Filesystem Queue A -server.register(namespace=source.id(input_filesystem_0), - description="Clear all items of the filesystem queue A.", - usage="clear", - "clear", - - fun (s) -> - begin - clear_queue(input_filesystem_0) - "Clearing done." - end - ) - -# Clear Filesystem Queue B -server.register(namespace=source.id(input_filesystem_1), - description="Clear all items of the filesystem queue B.", - usage="clear", - "clear", - - fun (s) -> - begin - clear_queue(input_filesystem_1) - "Clearing done." - end - ) - -# Seek Filesystem Queue A -server.register(namespace = source.id(input_filesystem_0), - description="seek to relative position in #{source.id(input_filesystem_0)}", - usage = "seek <duration in seconds>", - "seek", - fun(t) -> begin - log("Seeking #{t} sec") - t = float_of_string(default=0.,t) - ret = source.seek(input_filesystem_0, t) - "Seeked #{ret} seconds." - end -) - -# Seek Filesystem Queue B -server.register(namespace = source.id(input_filesystem_1), - description="seek to relative position in #{source.id(input_filesystem_1)}", - usage = "seek <duration in seconds>", - "seek", - fun(t) -> begin - log("Seeking #{t} sec") - t = float_of_string(default=0.,t) - ret = source.seek(input_filesystem_1, t) - "Seeked #{ret} seconds." - end -) \ No newline at end of file diff --git a/modules/liquidsoap/in_queue.liq b/modules/liquidsoap/in_queue.liq new file mode 100644 index 00000000..d8290f34 --- /dev/null +++ b/modules/liquidsoap/in_queue.liq @@ -0,0 +1,105 @@ +# +# Aura Engine (https://gitlab.servus.at/aura/engine) +# +# Copyright (C) 2017-2020 - The Aura Engine Team. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# # Called when some new metadata info is available +# def on_metadata_notification(meta) = +# filename = meta["filename"] +# # artist = meta["artist"] +# # title = meta["title"] +# system('#{list.assoc(default="", "install_dir", ini)}/guru.py --on_play "#{filename}"') +# end + + +##################################### +# QUEUE SOURCES # +##################################### + +# Create Sources +input_filesystem_0 = request.equeue(id="in_filesystem_0") +input_filesystem_1 = request.equeue(id="in_filesystem_1") + +# Apply ReplayGain Normalization +input_filesystem_0 = amplify(id="in_filesystem_0", 1., override="replay_gain", input_filesystem_0) +input_filesystem_1 = amplify(id="in_filesystem_1", 1., override="replay_gain", input_filesystem_1) + +# Add Event Handlers +input_filesystem_0 = on_metadata(id="in_filesystem_0", on_metadata_notification, input_filesystem_0) +input_filesystem_1 = on_metadata(id="in_filesystem_1", on_metadata_notification, input_filesystem_1) + + +##################################### +# SERVER FUNCTIONS # +##################################### + + +# Clear Default Queue A +server.register(namespace=source.id(input_filesystem_0), + description="Clear all items of the default queue A.", + usage="clear", + "clear", + + fun (s) -> + begin + clear_queue(input_filesystem_0) + "Clearing done." + end + ) + +# Clear Default Queue B +server.register(namespace=source.id(input_filesystem_1), + description="Clear all items of the default queue B.", + usage="clear", + "clear", + + fun (s) -> + begin + clear_queue(input_filesystem_1) + "Clearing done." + end + ) + +# Seek Filesystem Queue A +server.register(namespace = source.id(input_filesystem_0), + description="Seek to relative position in #{source.id(input_filesystem_0)}", + usage = "seek <duration in seconds>", + "seek", + + fun (t) -> + begin + log("Seeking #{t} sec") + t = float_of_string(default=0.,t) + ret = source.seek(input_filesystem_0, t) + "Seeked #{ret} seconds." + end + ) + +# Seek Filesystem Queue B +server.register(namespace = source.id(input_filesystem_1), + description="Seek to relative position in #{source.id(input_filesystem_1)}", + usage = "seek <duration in seconds>", + "seek", + + fun (t) -> + begin + log("Seeking #{t} sec") + t = float_of_string(default=0.,t) + ret = source.seek(input_filesystem_1, t) + "Seeked #{ret} seconds." + end + ) diff --git a/modules/liquidsoap/library.liq b/modules/liquidsoap/library.liq index fbcf50f6..0dc7881b 100644 --- a/modules/liquidsoap/library.liq +++ b/modules/liquidsoap/library.liq @@ -17,6 +17,44 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. + + +##################################### +# CLEAR SOURCE # +##################################### + +def clear_items(ns) = + ret = server.execute("#{source.id(ns)}.primary_queue") + ret = list.hd(default="", ret) + if ret == "" then + log("Queue cleared.") + (-1.) + else + log("There are still items in the queue, trying skip ...") + source.skip(ns) + (0.1) + end +end + +def clear_queue(ns) = + add_timeout(fast=false, 0.5, {clear_items(ns)}) +end + + +##################################### +# DYNAMIC SOURCES # +##################################### + +def create_dynamic_source(~skip=true, name) + log("Creating dynamic source '#{name}'") + track = get_process_lines("cat "^string.quote("next-track.txt")) + track = list.hd(default="", track) + dyn_source = request.dynamic.list( + { [request.create(track)] }) + dyn_source +end + + ##################### # stream to icecast # ##################### diff --git a/modules/liquidsoap/settings.liq b/modules/liquidsoap/settings.liq index b4c65889..57dbd3be 100644 --- a/modules/liquidsoap/settings.liq +++ b/modules/liquidsoap/settings.liq @@ -120,3 +120,10 @@ if use_alsa then end end +print("**************************************************************************************") +print(" AURA ENGINE - LIQUIDSOAP SETTINGS ") +print("**************************************************************************************") +print(" Engine Configuration Folder: #{engine_config_folder}") +print(" Station Fallback Playlist: #{fallback_station_playlist_path}") +print(" Station Fallback Directory: #{fallback_station_dir}") +print("**************************************************************************************") -- GitLab