Missing metadata in 2.3.x Rolling Release
gAlleb opened this issue · 16 comments
Description
Since #4039 (as pointed out by @vitoyucepi in #4072) we have no access to on_air
and on_air_timestamp
metadata.
P.S. The are important tags and used in some Now Playing APIs:
Liquidsoap version
Rolling Release 2.3.x
Hi and thanks for reporting.
As I said in the original discussion, I cleaned up the request implementation as part of 2.3.x
. That code was super old and wasn't reflecting how we do things now.
In particular, on_air
was a proper state of requests, beside idle
, resolving
, resolved
and destroyed
. This complicated the code a lot and did not reflect on the fact that a request can be used by multiple sources.
I understand the need to track what's on air at all time. We can definitely add the metadata back.
Here's my current thinking:
- Outputs, like sources, have a
last_metadata
method. This is a much better source for what's currently playing. - We can add
on_air
back on request, it would betrue
whenever the request is played in any source. - We can add back
on_air_timestamp
and make it the earliest time the request was ever played. - We can make the
on_air
metadata optional, enabled via a setting, and add a warning in the logs whenever a request is used in multiple sources whileon_air
is activated
This way we make it easier to transition to 2.3.x
when using on_air
while making it clear that this is not the best method to implement nowplaying
.
What do y'all think?
I think it's a valid thinking. It would be great if could keep on_air_timestamp
and keep the way it has worked - indicating when track started playing on a source. on_air
thingy is not that important imho, but a more representetive.
and did not reflect on the fact that a request can be used by multiple sources.
I kinda understand that) Kinda) Ok, it can be used by multiple sources - thus on_air_timestamp
can have different values depending from what source we request this value. Can't this be a thing?)
Outputs, like sources, have a last_metadata method. This is a much better source for what's currently playing.
I do personally use on_air_timestamp
to get the data along in two ways. One via on_metadata
:
def save_history(m, ~last=10)
np = { played_at = null_list("on_air_timestamp", m) }
end
s.on_metadata(save_history)
But I suppose I may change it then to
def save_history(_, ~last=10)
m = s.last_metadata() ?? []
np = { played_at = null_list("on_air_timestamp", m) }
end
s.on_track(save_history)
Ha I see.
Yeah, the data would be better grabbed on the output on_track
handler. There, you are sure to grab this exact time the output sees a given metadata.
Do I get it right?
We can add
on_air
back on request, it would be true whenever the request is played in any source.
on_air
will be changed when the same request is played on another source?
We can add back
on_air_timestamp
and make it the earliest time the request was ever played.
- Basically it means that we keep old functionality for
on_air_timestamp
if we are talking about a single source?
I made a mistake. I though on_air
was a boolean but it's actually a pretty-print of on_air_timestamp
.
Here's what I wrote for the migration doc:
on_air
andon_air_timestamp
metadata are deprecated. These values were never reliable. They are set at the request level whenrequest.dynamic
and all its derived sources start playing a request. However, a request can be used in multiple sources and the source using it can be used in multiple outputs or even not be actually being on the air if, for instance, it is not selected by aswitch
orfallback
.Instead, it is recommended to use output's
on_track
methods to track the metadata currently being played and the time at which it started being played. If needed, outputs (like sources) also have alast_metadata
method to return the metadata last seen on any given output.For backward compatibility and easier migration,
on_air
andon_air_timestamp
metadata can be enabled using therequest.deprecated_on_air_metadata
setting:request.deprecated_on_air_metadata := true
However, it is highly recommended to migrate your script to use output's
on_track
instead.
on_track methods to track the metadata currently being played
Something like this?
def f(m)
t = time.local()
end
s.on_track(f)
Yeah! Or to mimic on_air_timestamp
behavior while saving song history and now_playing info access:
np_timestamp = ref(0.0)
songHistory = ref([])
def save_history(_, ~last=10)
np_timestamp := time()
sh = {
np = { played_at = np_timestamp() }
}
songHistory := sh::songHistory()
if list.length(songHistory()) > last then
songHistory := list.prefix(last,songHistory())
end
end
s.on_track(save_history)
def get_NP_with_history(_)
data = {
np = { played_at = np_timestamp() },
song_history = songHistory()
}
http.response(...)
end
harbor.http.register.simple("/nowplaying", get_NP_with_history, port=8007, method="GET")
UPDATE. Yeah, this works nicely.
I don't get it. The <source>.last_metadata()
has no on_air
tag.
Why? I've been using this function for a long time and it worked.
def write_json_nowplaying(s)
def write_data()
def null_float(f)
f == infinity ? null() : f
end
def null_list(key, _list)
#list.assoc.mem(key, _list) ? list.assoc(key, _list) : null()
list.assoc.mem(key, _list) ? list.assoc(key, _list) : ""
end
m = s.last_metadata() ?? []
def get_cover_base64(~coverart_mime=null(), ~base64=true, m) =
c = metadata.cover(coverart_mime=coverart_mime, m)
if
null.defined(c)
then
c = null.get(c)
string.data_uri.encode(base64=base64, mime=c.mime, c)
else
""
end
end
np = {
now_playing = {
played_at = null_list("on_air_timestamp", m),
#played_at = np_timestamp(),
played_at_timestamp = null_list("on_air_timestamp", m),
#played_at_timestamp = np_timestamp(),
played_at_date_time = null_list("on_air", m),
duration = null_float(source.duration(s)),
elapsed = null_float(source.elapsed(s)),
remaining = null_float(source.remaining(s)),
playlist = null_list("playlist", m),
filename = null_list("filename", m),
song = {
artist = null_list("artist", m),
title = null_list("title", m),
album = null_list("album", m),
genre = null_list("genre", m),
cover = get_cover_base64(m)
}
},
song_history = songHistory()
}
if (m["jingle_mode"] != "true") then
send_mq_event(json.stringify(np, compact=true))
end
end
thread.run(write_data, every=1.0, fast=false)
end
write_json_nowplaying(s)
Now of course it doesn't have neither "on_air" nor "on_air_timestamp".
Ran through 2.2.5
Now of course it doesn't have neither "on_air" nor "on_air_timestamp".
That's the problem. I think <source>.on_metadata
, <source>.on_track
and <source>.last_metadata
should contain the start time for the <source>
.
Ha) I think too. that's the whole point of this thread)
In 2.2.5 there has been on_air
and on_air_timestamp
. Now in 2.3.0 there are gone. Inaccessible neither by on_metadata
request nor by last_metadata
.
Or I don't get something?
I've got another question. Is there a possibility to make on_air
with the power of time.local()
.
time.local()
is
dst : bool,
year_day : int,
week_day : int,
year : int,
month : int,
day : int,
hour : int,
min : int,
sec : int
and returns something like {dst=false, year_day=217, week_day=0, year=2024, month=8, day=4, hour=20, min=50, sec=44}
. Maybe it can be parsed somehow to look like on_air
tag If I want to use it on_track
?
I have just pushed a commit to the branch fixing the issue that adds on_air
and on_air_timestamp
to all outputs metadata, both via on_track
/on_metadata
/ last_metadata
and with the telnet metadata
command.
Hm. Doesn't seem to work.
Should I put this setting somewhere specific?
liquidsoap-1 | Error 5:
liquidsoap-1 | this value has no method `deprecated_on_air_metadata`
liquidsoap-1 | Its type is
liquidsoap-1 | {
Sorry, it is: settings.request.deprecated_on_air_metadata
I've got another question. Is there a possibility to make on_air with the power of time.local().
Ok, I got it)
g = time.string("%Y/%m/%d %H:%M:%S")
print(g)