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