From 908667267b1d39590fb0aa28da536c445ae49ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kier=C3=A1n=20Meinhardt?= Date: Tue, 8 Feb 2022 00:47:10 +0100 Subject: [PATCH] feat: radio powered by liquidsoap --- lib/streams.nix | 4 +- systems/makanek/radio/default.nix | 279 +++++++++--------------------- systems/makanek/tarot.nix | 2 + 3 files changed, 88 insertions(+), 197 deletions(-) diff --git a/lib/streams.nix b/lib/streams.nix index 822969f..cf4aa24 100644 --- a/lib/streams.nix +++ b/lib/streams.nix @@ -54,12 +54,12 @@ in [ stream = "https://c3lounge.de/radio/8000/radio.mp3"; } { - stream = "https://radio.kmein.de/lyrik/listen.ogg"; + stream = "https://radio.kmein.de/lyrik.ogg"; station = "Lyrik"; desc = "Lyrik-Lesung rund um die Uhr."; } { - stream = "https://radio.kmein.de/lyrikline/listen.ogg"; + stream = "https://radio.kmein.de/lyrikline.ogg"; station = "Lyrikline"; logo = "https://www.lyrikline.org/themes/lyrik/svg/Logo_lyrikline_pure.svg"; desc = "24/7 zufällige Wiedergaben von lyrikline.org."; diff --git a/systems/makanek/radio/default.nix b/systems/makanek/radio/default.nix index d17b026..b4e4896 100644 --- a/systems/makanek/radio/default.nix +++ b/systems/makanek/radio/default.nix @@ -1,212 +1,101 @@ { lib, pkgs, config, ... }: let - inherit (import ) tmpfilesConfig serveHtml; + icecastPassword = "hackme"; + lyrikline-poem = pkgs.writers.writeDash "lyrikline.sh" '' + set -efu - radioStore = "/var/lib/radio"; - htgenPort = 8080; - stations = { - lyrikline = { - streamPort = 8001; - mpdPort = 6601; - description = '' - Weltklang. Welt als ewiges Gedicht, das seine Schallspuren durch Raum und Zeit jagt. Endlose Zufallswiedergabe von lyrikline. — Listen to the sound of voices and poems permeating linguistic and geographic barriers, 24 hours per day. - ''; - }; - lyrik = { - streamPort = 8002; - mpdPort = 6602; - description = '' - Deutsche Lyrik, die du noch nicht gut genug kennst. Tritt in einen Fluss aus Reim und Maß; keine zwei Mal ist er derselbe. - ''; - }; - }; - mpd-add-with-tags = pkgs.writers.writeHaskell "mpd-add-with-tags" { - libraries = with pkgs.haskellPackages; [ optparse-generic libmpd ]; - } '' - {-# LANGUAGE DeriveGeneric, OverloadedStrings #-} - import Control.Monad (void) - import Data.String - import Network.MPD - import Options.Generic + html=$(mktemp) + trap clean EXIT + clean() { + rm "$html" + } - data Options = Options { url :: String, artist :: Maybe String, title :: Maybe String } - deriving (Generic) + lyrikline=https://www.lyrikline.org + random_route="$(${pkgs.curl}/bin/curl -sSL "$lyrikline/index.php/tools/getrandompoem" --data-raw 'lang=de' --compressed | ${pkgs.jq}/bin/jq -r .link)" + poem_url="$lyrikline$random_route" - instance ParseRecord Options + ${pkgs.curl}/bin/curl -sSL "$poem_url" > "$html" - main :: IO () - main = do - options <- getRecord "Add to MPD with tags" - void $ withMPD $ do - songId <- addId (fromString $ url options) Nothing - maybe (pure ()) (addTagId songId Artist . fromString) $ artist options - maybe (pure ()) (addTagId songId Title . fromString) $ title options + poem_file="$(${pkgs.gnugrep}/bin/grep -o 'https://.*\.mp3' "$html" | head -n1)" + + author="$(${pkgs.htmlq}/bin/htmlq -f "$html" --text '#gedicht-autor')" + title="$(${pkgs.htmlq}/bin/htmlq -f "$html" --text .gedicht-originaltitel)" + + echo "annotate:title=\"$title\",album=\"$poem_url\",artist=\"$author\":$poem_file" + ''; + stavenhagen-poem = pkgs.writers.writeDash "stavenhagen.sh" '' + base=https://www.deutschelyrik.de + author=$(${pkgs.curl}/bin/curl -sSL "$base" | ${pkgs.htmlq}/bin/htmlq option --attribute value | shuf -n1) + poem=$(${pkgs.curl}/bin/curl -sSL "$base/$author" | ${pkgs.htmlq}/bin/htmlq '#mnav2 li > a' --attribute href | shuf -n1) + + html=$(mktemp) + trap clean EXIT + clean() { + rm "$html" + } + + ${pkgs.curl}/bin/curl -sSL "$base/$poem" > "$html" + + printf "annotate:title=\"%s\",album=\"%s\",artist=\"%s\":$base/%s\n" \ + "$(${pkgs.htmlq}/bin/htmlq --text '.ce_text h1' -f "$html")" \ + "$base/$poem" \ + "$(${pkgs.htmlq}/bin/htmlq --text 'h1 + p em' -f "$html")" \ + "$(${pkgs.htmlq}/bin/htmlq 'audio source' --attribute src -f "$html")" + ''; +in { + # https://github.com/savonet/liquidsoap/issues/1043#issuecomment-593354427 + services.liquidsoap.streams.radio = pkgs.writeText "lyrikline.liq" '' + set("protocol.external.curl","${pkgs.curl}/bin/curl") + + def random_lyrikline() = + uri = list.hd(default="", get_process_lines("${lyrikline-poem}")) + request.create(uri, persistent=true) + end + + def random_stavenhagen() = + uri = list.hd(default="", get_process_lines("${stavenhagen-poem}")) + request.create(uri, persistent=true) + end + + lyrikline = mksafe(audio_to_stereo(request.dynamic(random_lyrikline))) + + output.icecast( + mount = '/lyrikline.ogg', + port = ${toString config.services.icecast.listen.port}, + password = "${icecastPassword}", + description = "lyrikline. listen to the poet (unofficial)", + %vorbis(quality = 1), + lyrikline + ) + + stavenhagen = mksafe(audio_to_stereo(request.dynamic(random_stavenhagen))) + + output.icecast( + mount = '/lyrik.ogg', + port = ${toString config.services.icecast.listen.port}, + password = "${icecastPassword}", + description = "Lyrik für alle – Neue Lust auf Lyrik | www.deutschelyrik.de", + %vorbis(quality = 1), + stavenhagen + ) ''; - mpcs = lib.mapAttrs (name: station: pkgs.writers.writeDashBin "mpc-${name}" '' - MPD_PORT=${toString station.mpdPort} ${pkgs.mpc_cli}/bin/mpc "$@" - '') stations; -in -{ - imports = [ ]; - nixpkgs.overlays = [ - (self: super: { htgen = super.callPackage {}; }) - ]; - - systemd.tmpfiles.rules = [ - (tmpfilesConfig { - type = "d"; - path = radioStore; - mode = "0755"; - user = config.users.extraUsers.radio.name; - age = "1d"; - }) - ]; - - users.extraUsers.radio = { - isSystemUser = true; - group = "radio"; - }; - users.groups.radio = {}; - - krebs.htgen.radio = { - port = htgenPort; - user.name = "radio"; - script = ''. ${pkgs.writers.writeDash "meinskript" '' - send200() { - printf 'HTTP/1.1 200 OK\r\n' - printf 'Content-Type: text/html; charset=UTF-8\r\n' - printf 'Connection: close\r\n' - printf '\r\n' - } - - case "$Method $Request_URI" in - "GET /lyrik/status") - send200 - video_id="$( - ${mpcs.lyrik}/bin/mpc-lyrik status -f %file% \ - | head -n1 \ - | grep -o 'id=[^&]*' \ - | sed 's/^id=//g' - )" - - ${pkgs.youtube-dl}/bin/youtube-dl -j "https://www.youtube.com/watch?v=$video_id" \ - | ${pkgs.jq}/bin/jq -r '"% [\(.title)](\(.webpage_url))\n\n\(.description)"' \ - | sed 's/$/ /g' \ - | ${pkgs.pandoc}/bin/pandoc -s - - exit - ;; - "GET /lyrikline/status") - send200 - - hash="$( - ${mpcs.lyrikline}/bin/mpc-lyrikline status -f '%file%' \ - | head -n 1 \ - | md5sum \ - | cut -d' ' -f 1 - )" - url="$(cat ${radioStore}/$hash)" - - echo "" - exit - ;; - esac - ''}''; - }; - - containers = lib.mapAttrs (name: station: { - autoStart = true; - config = {config, pkgs, ...}: { - services.mpd = { - enable = true; - network.port = station.mpdPort; - extraConfig = '' - log_level "default" - - audio_output { - name "${name}" - type "httpd" - encoder "vorbis" - port "${toString station.streamPort}" - bitrate "128" - format "44100:16:2" - always_on "yes" - tags "yes" - } - ''; - }; - }; - }) stations; - - environment.systemPackages = lib.attrValues mpcs; - - systemd.services.lyrikline = { - after = [ "container@lyrikline.service" ]; - wantedBy = [ "container@lyrikline.service" ]; - startAt = "*:00/5"; - serviceConfig.User = config.users.extraUsers.radio.name; - preStart = "${mpcs.lyrikline}/bin/mpc-lyrikline crop || :"; - script = '' - set -efu - - lyrikline=https://www.lyrikline.org - for _ in $(seq 1 10); do - random_route="$(${pkgs.curl}/bin/curl -sSL "$lyrikline/index.php/tools/getrandompoem" --data-raw 'lang=de' --compressed | ${pkgs.jq}/bin/jq -r .link)" - poem_url="$lyrikline$random_route" - - poem_file="$( - ${pkgs.curl}/bin/curl -sSL "$poem_url" \ - | grep -o 'https://.*\.mp3' \ - | head -n1 - )" - - hash="$(echo "$poem_file" | md5sum | cut -d' ' -f 1)" - echo "$poem_file ($hash) -> $poem_url" - echo "$poem_url" > "${radioStore}/$hash" - - ${mpcs.lyrikline}/bin/mpc-lyrikline add "$poem_file" - done - - ${mpcs.lyrikline}/bin/mpc-lyrikline play + services.icecast = { + enable = true; + hostname = "radio.kmein.de"; + admin.password = "hackme"; + listen.port = 6457; + extraConf = '' + + ${icecastPassword} + ''; }; - systemd.services.lyrik = { - after = [ "container@lyrik.service" ]; - wantedBy = [ "container@lyrik.service" ]; - preStart = "${mpcs.lyrik}/bin/mpc-lyrik crop || :"; - restartIfChanged = true; - serviceConfig.User = config.users.extraUsers.radio.name; - script = - let - invidious = "https://invidious.silkky.cloud"; - videoIds = import ; - streams = lib.concatMapStringsSep "\n" (id: "${invidious}/latest_version?id=${id}&itag=251") videoIds; - streamsFile = pkgs.writeText "hotrot" streams; - in '' - set -efu - ${mpcs.lyrik}/bin/mpc-lyrik add < ${toString streamsFile} - - ${mpcs.lyrik}/bin/mpc-lyrik crossfade 5 - ${mpcs.lyrik}/bin/mpc-lyrik random on - ${mpcs.lyrik}/bin/mpc-lyrik repeat on - ${mpcs.lyrik}/bin/mpc-lyrik play - ''; - }; - services.nginx.virtualHosts."radio.kmein.de" = { enableACME = true; forceSSL = true; - locations = lib.mkMerge ( - [ - { "/".extraConfig = serveHtml (import ./station-list.nix { inherit pkgs lib stations; }) pkgs; } - ] ++ (lib.mapAttrsToList (name: station: { - "= /${name}/status".proxyPass = "http://127.0.0.1:${toString htgenPort}"; - "= /${name}/listen.ogg".proxyPass = "http://127.0.0.1:${toString station.streamPort}"; - "= /${name}.ogg".return = "301 http://radio.xn--kiern-0qa.de/${name}/listen.ogg"; # legacy - }) stations) - ); + locations."/".proxyPass = "http://127.0.0.1:${toString config.services.icecast.listen.port}"; }; } diff --git a/systems/makanek/tarot.nix b/systems/makanek/tarot.nix index 5c8f598..6007487 100644 --- a/systems/makanek/tarot.nix +++ b/systems/makanek/tarot.nix @@ -12,6 +12,8 @@ let }; in { + imports = [ ]; + krebs.htgen.tarot = { port = tarotPort; user.name = "radio";