Created
May 24, 2021 20:39
-
-
Save gilou/45f636c04e7a74d5a01946a49167363e to your computer and use it in GitHub Desktop.
Liquidsoap function to avoid repetitions of tracks based on the filename
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Compare two pairs, according to snd value | |
def compare_snd(a, b) | |
if snd(a) == snd(b) then | |
0 | |
else | |
if snd(a) > snd(b) then | |
1 | |
else | |
-1 | |
end | |
end | |
end | |
# Returns pairs where snd is more than a given value | |
def more_than(p, ~value) | |
snd(p) > value | |
end | |
def log_track(m, ~played_recently) | |
now = gettimeofday() | |
log(level=3, 'Playing (#{now}): ' ^ m['initial_uri']) | |
played_recently := list.add((m['initial_uri'], now), !played_recently) | |
log(level=4, 'I have ' ^ string_of(list.length(!played_recently)) ^ " tracks") | |
end | |
# Function to be used as check_next for playlist*() | |
# uses a list to track played files, and returns false if the timestamp | |
# is too recent, unless it failed to select a track more than 3 times for a | |
# given source | |
# @param ~played_recently list of (played URIs, timestamp), optionnaly shared amongst multiple sources | |
# @param ~playlist_hunger list of (source, number of failures) to keep track of failed selections and avoid infinite loops | |
# @param ~delay_repeat how long between two repetitions of a file | |
# @param ~allowed_failures how many failures before giving up (can be high depending on the scenario) | |
# @param req request sent from check_next | |
def no_repeat( | |
~played_recently = ref [("empty", 0.0)], | |
~playlist_hunger = ref [("empty", 0)], | |
~delay_repeat = 30.0*60.0, | |
~allowed_failures = 200, | |
~notify_hunger = fun(src, track) -> (), | |
req | |
) | |
# This will be our final decision, do we play or skip the file? | |
do_play = ref true | |
# This tells us wether we played because we already skipped too many | |
meta = request.metadata(req) | |
name = meta['initial_uri'] | |
source = meta['source'] | |
# Gather all previously played information for the file | |
last_times = list.filter_assoc(name, !played_recently) | |
log(level=4,"checking "^ string_of(list.length(!played_recently)) ^ " entries for " ^ name) | |
# No result, good to go | |
if list.length(last_times) == 0 then | |
log(name ^ " was never played, not skipping") | |
else | |
# Sort descending by play time | |
last_times = list.rev(list.sort(compare_snd, last_times)) | |
log(level=4,"track played "^ string_of(list.length(last_times)) ^ " times") | |
# Last time is the first pair of the list | |
last_time = snd(list.hd(default=("", 0.0), last_times)) | |
if last_time < gettimeofday() - delay_repeat then | |
log(level=4,"last played at "^ string_of(last_time) ^", playing") | |
else | |
log(level=4,"track played in the last period") | |
nb_errors = list.hd(default=("", 0), list.filter_assoc(source, !playlist_hunger)) | |
log(level=4,"we already have #{string_of(snd(nb_errors))} failures for #{source}") | |
if fst(nb_errors) == "" then | |
log(level=4,"hunger not initialized for #{source}, setting to 1") | |
playlist_hunger := list.add((source, 1), !playlist_hunger) | |
do_play := false | |
else | |
log(level=4,"signaling a new hunger for #{source}, was set to " ^ string_of(snd(nb_errors))) | |
playlist_hunger := list.remove_assoc(source, !playlist_hunger) | |
playlist_hunger := list.add((fst(nb_errors), snd(nb_errors)+1), !playlist_hunger) | |
if snd(nb_errors) > allowed_failures then | |
notify_hunger(source, name) | |
log("Source #{source} failed more than #{allowed_failures} times, we repeat #{name} !") | |
else | |
log("Skipping track #{name}: played #{string_of(last_time)}") | |
do_play := false | |
end | |
end | |
end | |
end | |
# Remove old entries | |
size = list.length(!played_recently) | |
old = gettimeofday() - delay_repeat*2.0 | |
played_recently := list.filter(more_than(value=old), !played_recently) | |
deleted = size - list.length(!played_recently) | |
log(level=4, "cleaned #{deleted} entries") | |
# if we play, reset hunger counters | |
if !do_play then | |
log(level=4, "resetting hunger counter for #{source}") | |
playlist_hunger := list.remove_assoc(source, !playlist_hunger) | |
playlist_hunger := list.add((source, 0), !playlist_hunger) | |
end | |
!do_play | |
end | |
# Store pairs (filename, timestamp) of played files | |
played_recently = ref [("empty", 0.0)] | |
# Remember every time we skipped a given source to avoid an infinite loop | |
playlist_hunger = ref [("empty", 0)] | |
radio = playlist("mylist.m3u", check_next=no_repeat(played_recently=played_recently, playlist_hunger=playlist_hunger) | |
radio = on_track(log_track(played_recently=played_recently), radio) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment