From 93660fa379efbe81f4336843b18d41d0dc8d6a61 Mon Sep 17 00:00:00 2001 From: Scott Alfter Date: Sun, 17 Oct 2021 16:48:09 -0700 Subject: [PATCH] fully containerized, but should still be adaptable to non-container use --- Dockerfile | 10 ++++++++++ README.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ cleanup.sh | 4 +--- record.sh | 8 ++++---- rss.php | 16 ++++++++-------- 5 files changed, 78 insertions(+), 15 deletions(-) create mode 100644 Dockerfile create mode 100644 README.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c206d80 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM php:fpm +RUN apt-get update && apt-get install -y ffmpeg && rm -rf /var/lib/apt/lists/* +COPY record.sh cleanup.sh /usr/bin/ +COPY *.php /var/www/html/ +ENV BASEURL "$BASEURL" +ENV CHANNELS "${CHANNELS:-1}" +ENV BITRATE "${BITRATE:-32}" +RUN (cd /var/www/html && curl -L https://github.com/JamesHeinrich/getID3/archive/refs/tags/v1.9.21.tar.gz | tar xzf - && mv getID3-* getid3) +RUN (cd /var/www/html && curl -L https://github.com/mediaelement/mediaelement/archive/refs/tags/5.0.1.tar.gz | tar xzf - && mv mediaelement-* mediaelement) +VOLUME /var/www/html/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..d3a39dc --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +StreamShifter +============= + +Capture streaming audio from your favorite sources to build a custom podcast stream. I used this for a number of years on a bog-standard Linux/Nginx/PHP-FPM server, and have recently containerized it for use with Docker. It'll record live audio captured from a URL, transcoding it into a common format along the way. You can listen to the most recent stream as it records with HTTP live streaming, or you can listen later to the podcast. + +Standalone Instructions +----------------------- + +You'll need a webserver with PHP support, as well as curl and ffmpeg, which are used by the recording script. Unpack to an empty directory, and make sure the server is set to run index.php as an index file. + +record.sh gets called (most likely as a cronjob) to capture and save audio. It needs to be called with the CHANNELS and BITRATE environment variables defined as described below. + +cleanup.sh should be set up to run daily. + +rss.php depends on the BASEURL environment variable: + +```$baseurl=getenv("BASEURL");``` + +I don't know where offhand you'd set environment variables for a PHP script that's run by a webserver, so you might find it easier to hardcode your server's address here: + +```$baseurl="https://streams.alfter.us/";``` + +Docker Instructions +------------------- + +This container builds on the php:fpm container to include tools and scripts for capturing streaming audio, transcoding it to AAC, and storing it as a set of HTTP live streams and a podcast. + +Three environment variables are exposed to configure StreamShifter: + +* BASEURL: base URL under which the site will be served (required) +* CHANNELS: number of audio channels to encode (default 1) +* BITRATE: encoding bitrate, kbps (default: 32) + +The recordings and scripts to be served are stored in a named volume. Configure your webserver to serve it under a vhost by whatever means you would configure it to serve php:fpm. + +Start the PHP FPM server with something like this: + +```docker run -d --name streamshifter -e BASEURL=https://streamshifter.alfter.us/ -v streamshifter-data:/var/www/html salfter/streamshifter``` + +It runs on port 9000. You should set your webserver container to hand off PHP requests to it. + +To schedule recordings, use the record.sh script in a running container (use ```docker exec```) with four parameters, all required: + +* duration, seconds +* short name for the stream (no spaces, used as part of filenames) +* stream URL +* long name for the stream (may have spaces, written into stream metadata) + +Use whatever task scheduler your system provides (crontab, systemd timer unit, etc.) to run the script at the appropriate time. This example saves 10 seconds of the stream from KDWN, a talk-radio station in Las Vegas (streaming URL subject to change at their whim): + +```docker exec -it streamshifter record.sh 10 test "https://14983.live.streamtheworld.com/KDWNAMAAC.aac?tdsdk=js-2.9&pname=TDSdk&pversion=2.9&banners=320x50&sbmid=ddc0cb91-c9a7-46c4-bf53-85841c22c8e4" Test``` + +There is also a cleanup script you can run periodically that will remove everything more than one week old: + +```docker exec -it streamshifter cleanup.sh``` diff --git a/cleanup.sh b/cleanup.sh index d7cb787..8ba2316 100755 --- a/cleanup.sh +++ b/cleanup.sh @@ -1,5 +1,3 @@ #!/usr/bin/env bash source /etc/profile -#PATH=$PATH:/usr/local/bin -find /var/www/streamshifter/htdocs/audio -name \*.m4a -type f -mtime +7 -delete - +find /var/www/html/audio -name \*.m4a -type f -mtime +7 -delete diff --git a/record.sh b/record.sh index d6852df..fdebbfa 100755 --- a/record.sh +++ b/record.sh @@ -1,12 +1,12 @@ #!/usr/bin/env bash source /etc/profile -#PATH=$PATH:/usr/local/bin -cd /var/www/streamshifter/htdocs +mkdir -p /var/www/html/audio +cd /var/www/html find . -name $2-\*.ts -delete find . -name $2\*.m3u8 -delete -#transopt="aac -ac 1 -ab 32k -ar 22050" # AAC-LC -transopt="libfdk_aac -profile:a aac_he_v2 -ac 2 -ab 32k -ar 22050" # HE-AAC v2 +transopt="aac -ac ${CHANNELS} -ab ${BITRATE}k -ar 22050" # AAC-LC +#transopt="libfdk_aac -profile:a aac_he_v2 -ac ${CHANNELS} -ab ${BITRATE}k -ar 22050" # HE-AAC v2 (requires Gentoo, or a source build) curl -sm $1 "$3" 2>/dev/null | ffmpeg -i - -acodec $transopt -metadata title="$4 - `date +'%d %b %Y'`" -metadata artist="$4" -f segment -segment_list $2.m3u8 -segment_time 20 $2-%04d.ts 2>/dev/null ffmpeg -i $2.m3u8 -acodec copy -movflags +faststart -metadata title="$4 - `date +'%d %b %Y'`" -metadata artist="$4" audio/$2-`date +%Y%m%d`.m4a 2>/dev/null diff --git a/rss.php b/rss.php index 5567b4c..49ac1a4 100644 --- a/rss.php +++ b/rss.php @@ -2,7 +2,7 @@ require_once('./getid3/getid3/getid3.php'); $getid3=new getID3; - $baseurl="https://streamshifter.alfter.us/"; + $baseurl=getenv("BASEURL"); $doc = new DOMDocument("1.0", "UTF-8"); $root = $doc->createElement("rss"); @@ -15,12 +15,12 @@ $latest=0; $c=0; // sort entries into reverse chronological order - if ($h=opendir("audio")) + if ($h=opendir("/var/www/html/audio")) { while (($e=readdir($h))!==false) if (strlen($e)>4) if (substr_compare($e,".m4a",-4,4)===0) - $arr[$c++]=filemtime("audio/".$e)." ".$e; + $arr[$c++]=filemtime("/var/www/html/audio/".$e)." ".$e; closedir($h); } sort($arr, SORT_NUMERIC); @@ -32,7 +32,7 @@ $item=$doc->createElement("item"); // extract title from metadata - $fi=$getid3->analyze("audio/".$e); + $fi=$getid3->analyze("/var/www/html/audio/".$e); getid3_lib::CopyTagsToComments($fi); $item->appendChild($doc->createElement("title",$fi["comments_html"]["title"][0])); $item->appendChild($doc->createElement("author",$fi["comments_html"]["artist"][0])); @@ -45,16 +45,16 @@ $enclURL->value=$baseurl."audio/".$e; $encl->appendChild($enclURL); $enclLength=$doc->createAttribute("length"); - $enclLength->value=filesize($e); + $enclLength->value=filesize("/var/www/html/audio/".$e); $encl->appendChild($enclLength); $enclType=$doc->createAttribute("type"); $enclType->value="audio/mp4a-latm"; $encl->appendChild($enclType); $item->appendChild($encl); - $item->appendChild($doc->createElement("pubDate",gmdate(DATE_RSS,date(filemtime("audio/".$e))))); + $item->appendChild($doc->createElement("pubDate",gmdate(DATE_RSS,date(filemtime("/var/www/html/audio/".$e))))); $channel->appendChild($item); - if ($latestappendChild($doc->createElement("lastBuildDate",gmdate(DATE_RSS,date($latest))));