# monitor-dai.sh <url-to-hls-stream-including-tokens> <optional-record-dir> <delay>
#
-BASIC_CURL_PARAMS="--connect-timeout 10 --location --max-time 60 -Ss"
+BASIC_CURL_PARAMS="--connect-timeout 15 --location -Ss"
SCRIPT_NAME=$(basename "$0")
DATE_BSD="date -r "
# Used as default 10 seconds pause before retrying
TARGET_DURATION=10
-# No adblocks to record
-RECORD_AD_BLOCKS=3
+source "$(dirname $0)/record-config.sh" || fatal "Missing record-config.sh"
# Very limited function to resolve relative url
function resolveRelativeURL() {
# Extract first stream from HLS Master-Playlist
function resolveFirstStream() {
local hls="$1"
+ local bandwidth=-1
TMPFILE=$(mktemp) || fatal "Can not write tmp-file"
- url=$(curl $BASIC_CURL_PARAMS -o "$TMPFILE" "$hls" -w "%{url_effective}")
+ url=$(curl $BASIC_CURL_PARAMS --max-time 20 -o "$TMPFILE" "$hls" -w "%{url_effective}")
if [ $? -eq 0 ]; then
while IFS="" read line; do
- # First none comment is first element in playlist => we will use that stream
- re="^[^#].*$"
+ # Select proper bandwidth
+ re="^#EXT-X-STREAM-INF[:,]BANDWIDTH=([0-9]+)(,|$)"
if [[ "$line" =~ $re ]]; then
- echo $(resolveRelativeURL "$line" "$url")
- break
+ bandwidth="${BASH_REMATCH[1]}"
+ # First none comment is first element in playlist => we will use that stream
+ elif [[ "$bandwidth" -ne -1 && "$bandwidth" -lt "$RECORD_MAX_BANDWIDTH" ]]; then
+ re="^[^#].*$"
+ if [[ "$line" =~ $re ]]; then
+ echo $(resolveRelativeURL "$line" "$url")
+ break
+ fi
fi
done <"$TMPFILE"
else
local syncMode=-1 # (-1=get first, x=saw last at, 0=synched) In syncmode we try to request every second until we see a change to get a more precise time sync
local stream_time="" # unknown
- echo >&2 "$pretty_time: INFO: Started monitoring using: $stream"
+ echo >&2 "INFO: Started monitoring using: $stream"
while true; do
starttime=$(date +%s)
# Set time to real time first request
if [ -z "$stream_time" ]; then
+ syncMode=-1
if [ -n "$streamDelay" ]; then
stream_time=$(($starttime - $streamDelay))
pretty_time=$($DATE${stream_time%.*} +"%Y-%m-%d %H:%M:%S")
- echo >&2 "NOTICE: Using time-offset of -${streamDelay}s. Stream time is $pretty_time, real time is $(date ${realtime} +"%Y-%m-%d %H:%M:%S")"
+ echo >&2 "NOTICE: Using time-offset of -${streamDelay}s. Stream time is $pretty_time, real time is $($DATE${starttime} +"%Y-%m-%d %H:%M:%S")"
else
stream_time="$starttime"
pretty_time=$($DATE${stream_time%.*} +"%Y-%m-%d %H:%M:%S")
fi
TMPFILE=$(mktemp) || fatal "Can not write tmp-file"
- curl $BASIC_CURL_PARAMS -o "$TMPFILE" "$stream"
+ curl $BASIC_CURL_PARAMS --max-time 25 -o "$TMPFILE" "$stream"
if [ $? -eq 0 ]; then
while IFS="" read line; do
if [[ $curSeq -ne -1 ]]; then # we know media sequence
if [[ $curSeq -ge $nextSeq ]]; then # later sequence than we need
if [[ $doRecord -eq 1 ]]; then
- saveStreamBit "$stream" "$tags" "$stream_time" "$recordFolder"
+ saveStreamBit "$stream" "$tags" "$stream_time" "$recordFolder" &
fi
lastUsedSegment="$tags"
lastUsedStreamTime="$stream_time"
pretty_time=$($DATE${stream_time%.*} +"%Y-%m-%d %H:%M:%S")
if [[ $curSeq -ne $nextSeq ]]; then
echo >&2 "$pretty_time: WARN: Lost segments ($curSeq > $nextSeq)."
- stream_time=
+ stream_time=""
syncMode=-1
+ curSeq=-1
+ doRecord=0
+ doStopRecord=0
+ nextSeq=-1
+ tags=""
+ mode="UNKNOWN"
+ break
fi
#echo >&2 "$pretty_time: Current mode length: $curLength"
nextSeq=$(($curSeq + 1))
echo >&2 "$pretty_time: INFO: Seeing CUE-OUT (Ad block start) of duration: $value"
if [ "$mode" == "ADBLOCK" ]; then
echo >&2 "$pretty_time: ERROR: Already in ad block - extra cue-out after $curLength"
- #else
- # echo >&2 "$pretty_time: INFO: Ad-block started after $curLength seconds"
- fi
- mode="ADBLOCK"
- expectedAdLength=$value
- curLength=0
- if [ "$RECORD_AD_BLOCKS" -gt 0 ]; then
- if [ -z "$lastUsedSegment" ]; then
- echo >&2 "$pretty_time: INFO: Skipping recording since we did not see start of it."
- else
- RECORD_AD_BLOCKS=$(($RECORD_AD_BLOCKS - 1))
- echo >&2 "$pretty_time: INFO: Recording this adblock ($RECORD_AD_BLOCKS left)."
- saveStreamBit "$stream" "$lastUsedSegment" "$lastUsedStreamTime" "$recordFolder"
- doRecord=1
- doStopRecord=0
+ else
+ #echo >&2 "$pretty_time: INFO: Ad-block started after $curLength seconds"
+ mode="ADBLOCK"
+ expectedAdLength=$value
+ curLength=0
+ if [ "$RECORD_AD_BLOCKS" -gt 0 ]; then
+ if [ -z "$lastUsedSegment" ]; then
+ echo >&2 "$pretty_time: INFO: Skipping recording since we did not see start of it."
+ else
+ RECORD_AD_BLOCKS=$(($RECORD_AD_BLOCKS - 1))
+ echo >&2 "$pretty_time: INFO: Recording this adblock ($RECORD_AD_BLOCKS left)."
+ saveStreamBit "$stream" "$lastUsedSegment" "$lastUsedStreamTime" "$recordFolder" &
+ doRecord=1
+ doStopRecord=0
+ fi
fi
fi
elif [ "$tag" == "EXT-X-CUE-IN" ]; then
if [ "$expectedAdLength" != "$curLength" ]; then
echo >&2 "$pretty_time: WARN: Block was not the length expected ($curLength <> $expectedAdLength)"
fi
+ if [ "$doRecord" == "1" ]; then
+ echo >&2 "$pretty_time: INFO: Stopping recording."
+ doStopRecord=1
+ fi
+ curLength=0
elif [ "$mode" == "LIVE" ]; then
echo >&2 "$pretty_time: ERROR: Extra CUE-IN outside Ad-block after $curLength"
fi
mode="LIVE"
- curLength=0
- if [ "$doRecord" == "1" ]; then
- echo >&2 "$pretty_time: INFO: Stopping recording."
- doStopRecord=1
- fi
fi
fi
fi
# Fetch key if needed
if [[ -n "$keyURI" ]]; then
keyURI=$(resolveRelativeURL "$keyURI" "$streamURI")
- key=$(curl $BASIC_CURL_PARAMS "$keyURI" | hexdump -v -e '/1 "%02X"')
+ key=$(curl $BASIC_CURL_PARAMS --max-time 480 "$keyURI" | hexdump -v -e '/1 "%02X"')
# Decrypt on the fly
- curl $BASIC_CURL_PARAMS "$uri" | \
+ curl $BASIC_CURL_PARAMS --max-time 480 "$uri" | \
openssl aes-128-cbc -d -K "$key" -iv "$keyIV" -nosalt -out "$output"
else
# Fetch stream to file
- curl $BASIC_CURL_PARAMS -o "$output" "$uri"
+ curl $BASIC_CURL_PARAMS --max-time 480 -o "$output" "$uri"
fi
else
re="^#EXT-X-KEY:METHOD=(AES-128|NONE)(,URI=\"([^\"]*)\",IV=0x([0-9A-F]+))?"