Церковное служение
April 23

Трансляция потока музыки в сеть Интернет и на видеохостинги. Часть 3: Liquidsoap в контейнере


Настройка Liquidsoap в docker-контейнере

Настройка перед установкой

debian/ubuntu

for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

alma linux/centos stream/redos

sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install docker-ce docker-ce-cli containerd.io docker-compose-plugin

Запускаем службу Docker

sudo systemctl start docker.service
sudo systemctl enable docker.service

Структура файлов каталогов

/opt/radio				# Основной каталог
	    |
	    script.liq      # Скрипт работы Liquidsoap
	    music/			# Монтируемый каталог с медиа
	        |
	        songs       # Каталог с музыкой/подкастами
	          |
	          song1.mp3
	          song2.mp3
	          song3.mp3
	        jingles     # Каталог с джинглами
	          |
	          file1.mp3
	          file2.mp3
	          file3.mp3

Создаем каталоги

sudo mkdir -p /opt/radio/liquidsoap
sudo mkdir -p /opt/radio/music/{songs,jingles}

Редактируем файл /opt/radio/liquidsoap/docker-compose.yml

services:
	liquidsoap:
	  image: pltnk/liquidsoap
	  container_name: liquidsoap
	  restart: always
	  volumes:
	    - /opt/radio/script.liq:/etc/liquidsoap/script.liq
	    - /opt/radio/music:/music

Редактируем файл /opt/radio/script.liq

## Внизу файла замените:
## IP-АДРЕС-ХОСТА заменяем на внутренний ip-адрес хоста, где запущен сервис icecast2, куда мы будет транслировать аудиопоток
## ПАРОЛЬ-СТРИМА заменяем на пароль стрима, который мы указали в конфигурационном файле в icecast2
## ТОЧКА-МОНТИРОВАНИЯ заменяем на имя точки монтирования, которую мы указали в конфигурационном файле icecast2

# плейлист с джинглами
# указываем каталог с мр3-файлами джинглов. например, '/music/jingles'
jingles = mksafe(playlist(id="jingles",mode="randomize",reload_mode="watch",'/music/jingles'))

# дополнительная рандомизация
# отслеживает, чтобы последние два часа песни в рандоме не повторялись
l = playlog()
def check(r)
  m = request.metadata(r)
  if l.last(m) < 17200. then
    false
  else
    l.add(m)
    true
  end
end

# плейлист с треками стрима
# указываем каталог с мр3-файлами стрима. например, '/music/songs'
s = playlist(check_next=check,mode="randomize",'/music/songs')

# играем треки из каждого плейлиста, каждые 10 музыкальных треков - один джингл
radio = rotate(weights = [10, 1],[s,jingles])
radio = nrj(radio)

out = output.icecast(%mp3,
  host = "IP-АДРЕС-ХОСТА",
  port = 8000,
  password = "ПАРОЛЬ-СТРИМА",
  mount = "ТОЧКА-МОНТИРОВАНИЯ",
  fallible=true,
  radio
)

Размещаем медиа

В каталог /opt/radio/music/songs копируем все ваши mp3-файлы с музкальными композициями/подкастами
В каталог /opt/radio/music/jingles копируем все ваши джинглы

Запускаем liquidsoap

cd /opt/radio/liquidsoap
docker compose up

Ожидаем запуск и наблюдаем за работой

Сначала пройдёт процедура скачивания базового образа, затем будут выполнены команды запуска контейнера
Затем появятся журналы работы liquidsoap. Например, такие:

Container liquidsoap  Created
Attaching to liquidsoap
liquidsoap  | 2025/04/23 21:11:03 >>> LOG START
liquidsoap  | 2025/04/23 21:11:02 [main:3] Liquidsoap 2.0.3-1
liquidsoap  | 2025/04/23 21:11:02 [main:3] Using: graphics=[distributed with Ocaml] bytes=[distributed with OCaml 4.02 or above] pcre=7.4.6 sedlex=3.3 menhirLib=20211128 dtools=0.4.5 duppy=0.9.2 cry=0.6.7 mm=0.7.4 ogg=0.7.4 ogg.decoder=0.7.4 vorbis=0.8.0 vorbis.decoder=0.8.0 mad=0.5.3 dynlink=[distributed with Ocaml] lame=0.3.7 samplerate=0.1.6 taglib=0.3.10 camomile=1.0.2
liquidsoap  | 2025/04/23 21:11:02 [dynamic.loader:3] Could not find dynamic module for fdkaac encoder.
liquidsoap  | 2025/04/23 21:11:02 [clock:3] Using builtin (low-precision) implementation for latency control
liquidsoap  | 2025/04/23 21:11:03 [frame:3] Using 44100Hz audio, 25Hz video, 44100Hz main.
liquidsoap  | 2025/04/23 21:11:03 [frame:3] Video frame size set to: 1280x720
liquidsoap  | 2025/04/23 21:11:03 [frame:3] Frame size must be a multiple of 1764 ticks = 1764 audio samples = 1 video samples.
liquidsoap  | 2025/04/23 21:11:03 [frame:3] Targeting 'frame.duration': 0.04s = 1764 audio samples = 1764 ticks.
liquidsoap  | 2025/04/23 21:11:03 [frame:3] Frames last 0.04s = 1764 audio samples = 1 video samples = 1764 ticks.
liquidsoap  | 2025/04/23 21:11:03 [sandbox:3] Sandboxing disabled
liquidsoap  | 2025/04/23 21:11:03 [video.converter:3] Couldn't find preferred video converter: ffmpeg.
liquidsoap  | 2025/04/23 21:11:03 [audio.converter:3] Using samplerate converter: libsamplerate.
liquidsoap  | 2025/04/23 21:11:03 [test.mp3:3] Connecting mount test.mp3 for source@93.191.60.93...
liquidsoap  | 2025/04/23 21:11:03 [test.mp3:3] Connection setup was successful.
liquidsoap  | 2025/04/23 21:11:03 [clock.main:3] Streaming loop starts in auto-sync mode
liquidsoap  | 2025/04/23 21:11:03 [clock.main:3] Delegating synchronisation to CPU clock
liquidsoap  | 2025/04/23 21:11:03 [switch_2:3] Switch to switch_1.
liquidsoap  | 2025/04/23 21:11:03 [switch_1:3] Switch to mksafe.
liquidsoap  | 2025/04/23 21:11:03 [mksafe:3] Switch to safe_blank.
liquidsoap  | 2025/04/23 21:11:03 [decoder.id3v2:2] Error while decoding file tags: Invalid_argument("String.sub / Bytes.sub")
liquidsoap  | 2025/04/23 21:11:03 [jingles:3] Prepared "/music/jingles/Спасён_v1.mp3" (RID 1).
liquidsoap  | 2025/04/23 21:11:03 [mksafe:3] Switch to jingles with transition.
liquidsoap  | 2025/04/23 21:11:03 [songs:3] Prepared "/music/songs/Псалом 51 (на стихи Н.Басовского).mp3" (RID 0).
```

В этот момент можно перейти на icecast2 точку подключения и послушать, появился ли стрим.

И если всё ОК, звук есть, то завершите работу контейнера, нажав комбинацию клавиш Ctrl+C и дождитесь завершения работы контейнера.

Далее настроим автоматический запуск и работу нашего liquidsoap

Настройка работы liquidsoap, как сервис

Редактируем файл сервиса /etc/systemd/system/liquidsoap.service

[Unit]
Description=liquidsoap as a docker container
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c "docker compose -f /opt/radio/liquidsoap/docker-compose.yml up --detach"
ExecStop=/bin/bash -c "docker compose -f /opt/radio/liquidsoap/docker-compose.yml stop"

[Install]
WantedBy=multi-user.target

Обновляем сервисы

sudo systemctl daemon-reload

Запускаем liquidsoap, как службу

sudo systemctl start liquidsoap

Переключаем службу в автозапуск

sudo systemctl enable liquidsoap

Заключение

Остаётся только дать ссылку на документацию к Liquidsoap https://www.liquidsoap.info/doc-2.3.2/

Надеюсь, что статьи помогут вам в работе ваших интернет-радиостанций.

С Богом всё получится!