Skip to content

Instantly share code, notes, and snippets.

@GeekBrony
Last active July 13, 2023 14:52
Show Gist options
  • Star 27 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save GeekBrony/47d2212ed6408be816a2 to your computer and use it in GitHub Desktop.
Save GeekBrony/47d2212ed6408be816a2 to your computer and use it in GitHub Desktop.
Liquidsoap Radio System
###########################################################################################
# Liquidsoap Radio System - built by GeekBrony, based from Liquidsoap. #
###########################################################################################
# Primary Features:
# - AutoDJ that crossfades between songs and has a 1/10 chance of landing on a Station ID/Jingle/Ad.
# - AutoDJ also selects tracks from "High Priority", "Low Priority" and "New Songs" playlists randomly.
# - Live Shows (that automatically dump to a directory)
# - Mic to speak over the stream.
# - Requests via Telnet/Web Interface, which have a 80-90% chance of queuing up when a song ends.
# - Outputs in 96kbps, 128kbps, and 320kbps MP3 with ID3v2 tags.
#
# Planned Changes:
# - Fix Live Show Dumping creating a file upon start, which is unnecessary.
# - Queuing Playlists
# - Add alternate streams with only the playlist running, for people who just want music.
#
#################################
# <BEGIN CONFIGURATION SECTION> #
#################################
# Please edit to your needs, or else you will get errors upon startup #
# Music
# 'musicdir' and 'jingledir' can be a local directory/playlist OR a playlist (pls/m3u) file on the internet.
musicdir = "/store/music/"
jingledir = "/store/ident/"
# Music Selection Mode
# random : Every playlist reload, shuffle.
# randomize : Every song, shuffle.
# normal : Sequential order.
pls_mode = "random"
# The variable 'securityuri' has to be a local file otherwise you will get an error saying 'That source is fallible!'
securityuri = "/var/www/bdn/httpdocs/maintenance/elevator.mp3"
# Playlists
# Hits
#hits_list = "/srv/pls/current_hits.m3u"
# Music Selection Mode
# random : Every playlist reload, shuffle.
# randomize : Every song, shuffle.
# normal : Sequential order.
#hits_mode = "random"
# New Songs
new_song_list = "/store/new_music/"
# Music Selection Mode
# random : Every playlist reload, shuffle.
# randomize : Every song, shuffle.
# normal : Sequential order.
new_mode = "random"
# Mellow Mondays
mellow_list = "/srv/pls/mellow_mondays.m3u"
# Music Selection Mode
# random : Every playlist reload, shuffle.
# randomize : Every song, shuffle.
# normal : Sequential order.
mellow_mode = "random"
# High Priority Songs
hp_list = "/var/www/bdn/httpdocs/api/voting/high_priority.pls"
hp_mode = "randomize"
# Low Priority Songs
lp_list = "/store/low_priority/"
lp_mode = "randomize"
# Live Stream
# Live Mount URL
livem = "http://localhost:8000/stream-live"
# Buffer
live_buffer = 5.
live_buffer_max = 15.
# Microphone (Speak over music)
# Microphone Mount URL
micm = "http://localhost:8000/stream-mic"
# Buffer
mic_buffer = 2.
mic_buffer_max = 10.
# Volume
background_volume = 0.15
# Logging
log_to_stdout = true
log_to_file = true
logpath = "/home/server/liquidsoapconfig/log.log"
loglevel = 3
# Telnet
enable_telnet = true
telnetbindaddr = "127.0.0.1"
telnetport = 2345
# Live Dumping
# To disable live dumping, comment out lines 335-340 with '#'
# Make sure the directory exists and/or has permission to make one.
# %Y = year, %m = month, %d = day, %H = hour, %M = minute, %S = seconds
#dump_mp3_file = "/var/www/brony.co/htdocs/rdmp/LIVE_%Y-%m-%d_%H-%M-%S.mp3"
# Liquidsoap will add this string after request metadata
# Example: "Artist - Title (Remix) [RQ]"
# request_m = "[RQ]"
# NOT IN USE
# Last.fm Scrobbling
lfm_enable = true
lfmuser = ""
lfmpass = ""
# Icecast Information:
hostname = "127.0.0.1"
iceport = 8000
passw = "[REDACTED]"
# Stream Name
icename = "Radio"
# Stream Genre
icegenre = "Pony"
# Description
icedesc = "Best Variety Pony Hits + Live Shows"
# Website URL
iceurl = "localhost"
###############################
# <END CONFIGURATION SECTION> #
###############################
#- DO NOT EDIT AFTER THIS LINE UNLESS YOU KNOW WHAT YOU ARE DOING -#
# For requests, add variable 'request_m' after the metadata
def append_title_request(m) =
# Grab the current title
title = m["title"]
# Return a new title metadata, which is disabled (for now).
[("title","#{title}")]
end
def crossfade(s)
#duration is automatically overwritten by metadata fields passed in
#with audio
s = fade.in(type="sin", duration=4., s)
s = fade.out(type="sin", duration=5., s)
fader = fun (a,b) -> add(normalize=false,[b,a])
cross(fader,s)
end
def smooth_add(~delay=0.5,~p=0.2,~normal,~special)
d = delay
fade.final = fade.final(duration=d*2.)
fade.initial = fade.initial(duration=d*2.)
q = 1. - p
c = amplify
fallback(track_sensitive=false,
[special,normal],
transitions=[
fun(normal,special)->
add(normalize=false,
[c(p,normal),
c(q,fade.final(type="sin",normal)),
sequence([blank(duration=d),c(q,special)])]),
fun(special,normal)->
add(normalize=false,
[c(p,normal),
c(q,fade.initial(type="sin",normal))])
])
end
# Add a skip function to a source
# when it does not have one
# by default
def add_skip_command(~command,s)
# Register the command:
server.register(
usage="skip",
description="Skip the current song in source.",
command,
fun(_) -> begin source.skip(s) "Done!" end
)
end
# Enable logging on Standard Output and set logging level.
set("log.stdout",log_to_stdout)
set("log.level",loglevel)
# Enable logging to file. See variable: logpath
set("log.file",log_to_file)
set("log.file.path",logpath)
# Allow requests from Telnet (Liquidsoap Requester)
set("server.telnet",enable_telnet)
set("server.telnet.bind_addr",telnetbindaddr)
set("server.telnet.port",telnetport)
set("harbor.bind_addr","0.0.0.0")
def lastfm.scrobble(~user,~password,m) =
if lfm_enable then
audioscrobbler.submit(user=user,password=password,m)
audioscrobbler.nowplaying(user=user,password=password,m)
end
# Only enable this if you really understand what everything does in these three PHP files
# Which at the moment, only GeekBrony knows what it does.
meta_database = true
if meta_database then
str = base64.encode(json_of(compact=true, m))
system("php /var/www/bdn/httpdocs/api/countPlayed.php \"#{str}\"")
system("php /var/www/bdn/httpdocs/api/extra_info.php \"#{str}\"")
system("php /var/www/bdn/httpdocs/api/album_art.php \"#{str}\"")
end
end
myplaylist = playlist(id="playlist",length=3600.0,default_duration=1800.0,timeout=10.0,
mode=pls_mode,reload=480,reload_mode="seconds",mime_type="audio/x-mpegurl",musicdir)
#hits = playlist(id="playlist",length=3600.0,default_duration=1800.0,timeout=10.0,
# mode=hits_mode,reload=480,reload_mode="seconds",mime_type="audio/x-mpegurl",hits_list)
new = playlist(id="playlist",length=3600.0,default_duration=1800.0,timeout=10.0,
mode=new_mode,reload=480,reload_mode="seconds",mime_type="audio/x-mpegurl",new_song_list)
mellow = playlist(id="playlist",length=3600.0,default_duration=1800.0,timeout=10.0,
mode=mellow_mode,reload=480,reload_mode="seconds",mime_type="audio/x-mpegurl",mellow_list)
hp = playlist(id="playlist",length=3600.0,default_duration=1800.0,timeout=10.0,
mode=hp_mode,reload=480,reload_mode="seconds",mime_type="audio/x-mpegurl",hp_list)
lp = playlist(id="playlist",length=3600.0,default_duration=1800.0,timeout=10.0,
mode=lp_mode,reload=480,reload_mode="seconds",mime_type="audio/x-mpegurl",lp_list)
# myplaylist = smart_crossfade(myplaylist)
# Request Queue from Telnet (Liquidsoap Requester)
requests = audio_to_stereo(request.queue(id="requests"))
requests = map_metadata(append_title_request, requests)
jingles = nrj(playlist(jingledir))
liveshow = nrj(audio_to_stereo(input.http(buffer=live_buffer,max=live_buffer_max,livem)))
microphone = nrj(audio_to_stereo(input.http(buffer=mic_buffer,max=mic_buffer_max,micm)))
# Start building the feed with music
# Randomize the source choices
radio = random(id="randomizer", weights=[7000, 2399, 400, 1, 200], [requests, myplaylist, hp, lp, new])
radio=fallback(track_sensitive=true,[switch([
#Monday = 1w#
({ (1w) and 8h-12h }, mellow),
#({ (5w) and 14h-18h }, ),
]), radio])
# Add fallbacks and jingles
security = nrj(single(securityuri))
radio = rotate(weights = [5, 1],[radio, jingles])
radio = fallback(id="switcher",track_sensitive = false, [liveshow, radio, security]) #myplaylist
#radio = fallback(track_sensitive = false,[liveshow, radio])
# Add a skip command for use via telnet
add_skip_command(command="skip", radio)
# Add an announcement queue.
announce = audio_to_stereo(request.queue(id="announce"))
# Talk over stream using microphone mount.
radio = smooth_add(delay=0.8, p=background_volume, normal=radio, special=microphone)
# Make quick announcements on the fly with this.
radio = smooth_add(delay=0.8, p=background_volume, normal=radio, special=announce)
radio = on_metadata(lastfm.scrobble(user=lfmuser,password=lfmpass), radio)
radio = mksafe(radio)
# Setup the output streams, and autodump to a webserver:
output.icecast(%mp3(samplerate=44100,stereo=true,id3v2=true,bitrate=128),id="ToVorbis",name=icename,
host=hostname,port=iceport,password=passw,mount="stream-128.mp3",genre=icegenre,icy_metadata="true",
description="#{icedesc} (Normal)",url=iceurl,public=true,radio)
output.icecast(%mp3(samplerate=48000,stereo=true,id3v2=true,bitrate=320),id="ToVorbis",name=icename,
host=hostname,port=iceport,password=passw,mount="stream-320.mp3",genre=icegenre,icy_metadata="true",
description="#{icedesc} (Ultra Quality)",url=iceurl,public=true,radio)
output.icecast(%mp3(samplerate=32000,stereo=false,id3v2=true,bitrate=96),id="ToVorbis",name=icename,
host=hostname,port=iceport,password=passw,mount="stream-96.mp3",genre=icegenre,icy_metadata="true",
description="#{icedesc} (Mobile)",url=iceurl,public=true,mean(radio))
# radio = on_shutdown(fun(_) -> fade.final(type="sin", duration=3., radio))
# Fail attempt at fading out on shutdown, will try to fix later.
#output.file(
# %mp3(bitrate=192),fallible=true,
# on_start={log("Starting live dump.")},
# reopen_on_metadata=false,"#{dump_mp3_file}",
# on_close=fun(_) -> (log("Stopped live dump.")),
#liveshow)
# END OF SCRIPT #
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment