sintonia/playout/libretime_playout/liquidsoap/1.4/ls_script.liq

230 lines
7.5 KiB
Plaintext
Raw Permalink Normal View History

boot_timestamp = string_of(gettimeofday())
2011-01-17 16:30:45 +01:00
web_stream_enabled = ref false
web_stream_id = ref '-1'
show_name = interactive.string("show_name", "")
2011-01-17 16:30:45 +01:00
2020-12-24 23:18:15 +01:00
dynamic_metadata_callback = ref fun (~new_track=false, s) -> begin () end
just_switched = ref false
2011-03-07 20:04:47 +01:00
%include "ls_lib.liq"
2011-01-17 16:30:45 +01:00
sources = ref []
source_id = ref 0
def create_source()
this_source_id = !source_id
l = request.equeue(id="s#{this_source_id}", length=0.5)
l = audio_to_stereo(id="queue_src", l)
l = cue_cut(l)
l = amplify(1., override="replay_gain", l)
fix(playout): when shows ends, next shows starts without fade-in/fade-out (#2412) Tracks are not fading with the crossfade function which leads to hard cuts at the end of tracks and shows. Therefore the explicit fade functions are used. In Liquidsoap version 1.4.3. crossfade is implemented as a cross with a custom transition (fade_in and fade_out). https://github.com/savonet/liquidsoap/blob/9f730f2c5f6195e37a761f7b1eb5a74395d22b18/src/libs/fades.liq#L433-L436 The "duration" argument is passed through to the cross function. In the implementation of the cross operator the value duration is used to determine how log the crossfade should take. It is set to the cross_lenght parameter https://github.com/savonet/liquidsoap/blob/f075905715584bdc236fe2078e26352a5b7f1c5f/src/operators/cross.ml#L30-L34 This can be overwritten with metadata, but the current annotation does not include a "override_duration" field so in our case it is always 0. https://github.com/savonet/liquidsoap/blob/f075905715584bdc236fe2078e26352a5b7f1c5f/src/operators/cross.ml#L186-L198 So I assume the crossfade is starting to fade.out the track but because the duration is set to 0. the "cross" is completed immediately and the next source of the queue is started. Our queues do only ever contain one track at a time so there is no next source to play. The next queue is activated and the same happens for the fade.in. Replacing the crossfade with a fade.in/out removes this time boundary as there is no longer a "cross" function involved. Until the tag 3.0.0-alpha.8 there was a custom crossfade_airtime function. In tag 3.0.0-alpha.9 it was replaced with the crossfade function but was unable to find why. https://github.com/libretime/libretime/blob/ecd302068c443af3eaa44bd3f81f45cdd88b2155/python_apps/pypo/liquidsoap/1.4/ls_script.liq#LL76C9-L76C18 Co-authored-by: Marvin <Marvin>
2023-03-02 20:20:09 +01:00
# Fade the tracks to avoid hard cuts in between two tracks and at the end of the shows.
# Liquidsoap reads the fade in/out durations from the annotation "liq_fade_in/out" which
# value can be set via Libretime settings.
l = fade.in(l)
l = fade.out(l)
l = on_metadata(notify_queue, l)
sources := list.append([l], !sources)
server.register(namespace="queues",
"s#{this_source_id}_skip",
fun (s) -> begin log("queues.s#{this_source_id}_skip")
2020-12-24 23:18:15 +01:00
clear_queue(l)
"Done"
end)
source_id := !source_id + 1
end
create_source()
create_source()
create_source()
create_source()
queue = add(!sources, normalize=false)
pair = insert_metadata(queue)
dynamic_metadata_callback := fst(pair)
queue = snd(pair)
output.dummy(fallible=true, queue)
2012-12-03 17:06:56 +01:00
http = input.http_restart(id="http")
http = cross_http(http_input_id="http",http)
output.dummy(fallible=true, http)
stream_queue = http_fallback(http_input_id="http", http=http, default=queue)
stream_queue = map_metadata(id="map_metadata:schedule", update=false, append_title, stream_queue)
ignore(output.dummy(stream_queue, fallible=true))
def web_stream_set_id(value)
web_stream_id := value
string_of(!web_stream_id)
end
def web_stream_get_id()
string_of(!web_stream_id)
end
server.register(namespace="sources",
description="Start webstream source",
"start_web_stream",
fun (s) -> begin log("sources.start_web_stream")
notify([("schedule_table_id", !web_stream_id)])
web_stream_enabled := true "enabled" end)
server.register(namespace="sources",
description="Stop webstream source",
"stop_web_stream",
fun (s) -> begin log("sources.stop_web_stream") web_stream_enabled := false "disabled" end)
server.register(namespace="web_stream",
description="Set the web stream id",
"set_id",
fun (s) -> begin log("web_stream.set_id") web_stream_set_id(s) end)
server.register(namespace="web_stream",
description="Get the web stream id",
"get_id",
fun (s) -> begin log("web_stream.get_id") web_stream_get_id() end)
default = amplify(id="silence_src", 0.00001, noise())
def map_message_offline(m) =
[("title", message_offline())]
2020-12-24 23:18:15 +01:00
end
default = map_metadata(id="map_metadata:offline", map_message_offline, default)
ignore(output.dummy(default, fallible=true))
input_main_streaming = ref false
input_show_streaming = ref false
schedule_streaming = ref false
2012-03-05 23:04:13 +01:00
def start_input_main() input_main_streaming := true end
def stop_input_main() input_main_streaming := false end
def start_input_show() input_show_streaming := true end
def stop_input_show() input_show_streaming := false end
def start_schedule() schedule_streaming := true; just_switched := true end
def stop_schedule() schedule_streaming := false end
def update_source_status(sourcename, status) =
gateway("live '#{sourcename}' '#{status}'")
end
def input_main_on_connect(header) update_source_status("master_dj", true) end
def input_main_on_disconnect() update_source_status("master_dj", false) end
def input_show_on_connect(header) update_source_status("live_dj", true) end
def input_show_on_disconnect() update_source_status("live_dj", false) end
def make_input_func(secure)
if secure then
input.harbor.ssl
else
input.harbor
end
end
def make_input_auth_handler(input_name)
def auth_handler(user, password)
log("user '#{user}' connected", label="#{input_name}_input")
# Check auth based on return value from auth script
ret = test_process("libretime-playout-notify live-auth '#{input_name}' '#{user}' '#{password}'")
if ret then
log("user '#{user}' authenticated", label="#{input_name}_input")
else
log("user '#{user}' auth failed", label="#{input_name}_input",level=2)
end
ret
end
auth_handler
end
s = switch(id="switch:blank+schedule",
2013-03-01 18:48:32 +01:00
track_sensitive=false,
transitions=[transition_default, transition],
[({!schedule_streaming}, stream_queue), ({true}, default)]
2013-03-01 18:48:32 +01:00
)
s = if input_show_port != 0 and input_show_mount != "" then
input_show_func = make_input_func(input_show_secure)
input_show_source =
2013-03-01 18:48:32 +01:00
audio_to_stereo(
input_show_func(id="harbor:input_show",
input_show_mount,
port=input_show_port,
auth=make_input_auth_handler("show"),
2013-03-01 18:48:32 +01:00
max=40.,
on_connect=input_show_on_connect,
on_disconnect=input_show_on_disconnect))
2013-03-01 18:48:32 +01:00
ignore(output.dummy(input_show_source, fallible=true))
2013-03-01 18:48:32 +01:00
switch(id="switch:blank+schedule+show",
2013-03-01 18:48:32 +01:00
track_sensitive=false,
transitions=[transition, transition],
[({!input_show_streaming}, input_show_source), ({true}, s)]
2013-03-01 18:48:32 +01:00
)
else
s
end
s = if input_main_port != 0 and input_main_mount != "" then
input_main_func = make_input_func(input_main_secure)
input_main_source =
audio_to_stereo(
input_main_func(id="harbor:input_main",
input_main_mount,
port=input_main_port,
auth=make_input_auth_handler("main"),
max=40.,
on_connect=input_main_on_connect,
on_disconnect=input_main_on_disconnect))
2013-03-01 18:48:32 +01:00
ignore(output.dummy(input_main_source, fallible=true))
2013-03-01 18:48:32 +01:00
switch(id="switch:blank+schedule+show+main",
2013-03-01 18:48:32 +01:00
track_sensitive=false,
transitions=[transition, transition],
[({!input_main_streaming}, input_main_source), ({true}, s)]
2013-03-01 18:48:32 +01:00
)
else
s
end
2012-12-03 17:06:56 +01:00
server.register(namespace="sources",
description="Stop main input source.",
usage="stop_input_main",
"stop_input_main",
fun (s) -> begin log("sources.stop_input_main") stop_input_main() "Done." end)
server.register(namespace="sources",
description="Start main input source.",
usage="start_input_main",
"start_input_main",
fun (s) -> begin log("sources.start_input_main") start_input_main() "Done." end)
server.register(namespace="sources",
description="Stop show input source.",
usage="stop_input_show",
"stop_input_show",
fun (s) -> begin log("sources.stop_input_show") stop_input_show() "Done." end)
server.register(namespace="sources",
description="Start show input source.",
usage="start_input_show",
"start_input_show",
fun (s) -> begin log("sources.start_input_show") start_input_show() "Done." end)
server.register(namespace="sources",
description="Stop schedule source.",
usage="stop_schedule",
"stop_schedule",
fun (s) -> begin log("sources.stop_schedule") stop_schedule() "Done." end)
server.register(namespace="sources",
description="Start schedule source.",
usage="start_schedule",
"start_schedule",
fun (s) -> begin log("sources.start_schedule") start_schedule() "Done." end)