savonet / liquidsoap

Liquidsoap is a statically typed scripting general-purpose language with dedicated operators and backend for all thing media, streaming, file generation, automation, HTTP backend and more.
http://liquidsoap.info
GNU General Public License v2.0
1.4k stars 128 forks source link

How best to allow user-selectable playlists? #1491

Open Sophira opened 3 years ago

Sophira commented 3 years ago

First off, thank you so much for Liquidsoap!

I want to set up a small, private stream in which a small number of users will have access to create/modify playlist directories in ~/playlists/, and I'd like the stream to play from one of these directories at any given time but to allow the user to change which directory is being used to play from. I am using Liquidsoap v1.4.3 for amd64 via the .deb file download on the GitHub releases page.

I've tried a few different methods, but all of them have some downsides:

  1. I've tried making a .m3u file which has a single line in it pointing to the playlist directory to use, and then pointing playlist() to this with reload_mode = "watch". The Wikipedia article on the M3U format suggests that this should work in Example 2 of the Examples section, but in practice it seems like Liquidsoap treats it as a track and tries to open it as one rather than descending to play the files in the directory. I'm not sure if this is a bug in Liquidsoap, non-standard behaviour in other players, or simply an error in the wiki.

  2. I've tried replacing the .m3u file with a symlink to the playlist directory to use, and then using ln -sfn to change where the symlink points to. This sort of works, but when I change the playlist and Liquidsoap finishes playing a file from the old playlist, I get this scary line in the log:

2021/02/13 11:09:02 [current-playlist:2] Finished with a non-existent file?! Something may have been moved or destroyed during decoding. It is VERY dangerous, avoid it!

So despite this seeming to do the right thing, I am assuming this is a Bad Idea.

  1. I have tried using an interactive.string called "requested_playlist", and then including #{requested_playlist()} in the playlist string. However, the string appears to be interpolated before it's passed to playlist(), so it never changes even when the interactive string is updated (and even if it was, the playlist would need to be reloaded).

  2. I have tried using an interactive.string combined with a custom protocol to force requested_playlist() to be re-evaluated each time:

def dynplaylist_protocol(~rlog,~maxtime,arg) =
  ["~/playlists/#{requested_playlist()}"]
end
add_protocol("dynplaylist", dynplaylist_protocol)

music = playlist("dynplaylist:dummy", reload = 10, reload_mode = "seconds")

This also kind of works, but unfortunately it's impossible to use reload_mode = "watch" with it because it is no longer a filesystem entry, so I have to use the method of reloading every 10 seconds, which I'd like to avoid if possible.

Is there a method I'm missing? It seems like this is something that should be fairly simple to do, but it seems surprisingly difficult.

rrolla commented 3 years ago

One solution is just read that switched directory and from that create playlist.m3u

#!/bin/bash

rm radio/playlist.m3u

find $1 -name "*.flac" -o -name "*.mp3" -print0 | while read -d $'\0' file
do 
  echo $file >> radio/playlist.m3u
done

call like this

./updateTunes.sh mydirectory

untested code