pluginengine01 / play-stream.sh
krystv's picture
Upload play-stream.sh
2753d8f verified
#!/usr/bin/env bash
# Stream playback helper — takes BEX stream JSON output and plays with mpv/ffplay
#
# Usage:
# ./play-stream.sh <stream_json>
# echo '{"manifest_url":"...", "headers":[...]}' | ./play-stream.sh
# ./play-stream.sh --from-file stream_output.json
#
# Supports:
# - HLS (.m3u8) streams
# - Direct MP4/MKV URLs
# - Custom headers (Referer, User-Agent, etc.)
# - mpv (preferred) or ffplay (fallback)
#
# The script extracts the stream URL and headers from the BEX engine's
# resolve-stream JSON output and constructs the correct player command.
set -euo pipefail
# Read JSON from stdin, argument, or file
if [ "${1:-}" = "--from-file" ] && [ -f "${2:-}" ]; then
JSON=$(cat "$2")
elif [ -n "${1:-}" ] && [ "${1:-}" != "-" ]; then
JSON="$1"
else
JSON=$(cat)
fi
if [ -z "$JSON" ]; then
echo "Error: No stream JSON provided"
echo "Usage: ./play-stream.sh '<json>' or pipe JSON to stdin"
exit 1
fi
# Parse JSON with jq (or python fallback)
if command -v jq &>/dev/null; then
STREAM_URL=$(echo "$JSON" | jq -r '.manifest_url // .videos[0].url // empty')
# Extract headers as key: value pairs
HEADERS=$(echo "$JSON" | jq -r '.headers[]? | "\(.key): \(.value)"')
FORMAT=$(echo "$JSON" | jq -r '.format // "hls"')
LABEL=$(echo "$JSON" | jq -r '.label // "Stream"')
elif command -v python3 &>/dev/null; then
STREAM_URL=$(echo "$JSON" | python3 -c "
import json, sys
d = json.load(sys.stdin)
url = d.get('manifest_url') or (d.get('videos', [{}])[0].get('url', ''))
print(url)
")
HEADERS=$(echo "$JSON" | python3 -c "
import json, sys
d = json.load(sys.stdin)
for h in d.get('headers', []):
print(f\"{h['key']}: {h['value']}\")
")
FORMAT=$(echo "$JSON" | python3 -c "
import json, sys
d = json.load(sys.stdin)
print(d.get('format', 'hls'))
")
LABEL="Stream"
else
echo "Error: requires 'jq' or 'python3' to parse JSON"
exit 1
fi
if [ -z "$STREAM_URL" ]; then
echo "Error: Could not extract stream URL from JSON"
echo "JSON received:"
echo "$JSON" | head -20
exit 1
fi
echo "=== BEX Stream Player ==="
echo "URL: $STREAM_URL"
echo "Format: $FORMAT"
echo "Label: $LABEL"
echo ""
# Build header arguments
MPV_HEADERS=""
FFPLAY_HEADERS=""
while IFS= read -r header; do
if [ -n "$header" ]; then
KEY=$(echo "$header" | cut -d: -f1 | xargs)
VALUE=$(echo "$header" | cut -d: -f2- | xargs)
MPV_HEADERS="${MPV_HEADERS}${KEY}: ${VALUE}\r\n"
FFPLAY_HEADERS="${FFPLAY_HEADERS}-headers \"${KEY}: ${VALUE}\r\n\" "
echo "Header: $KEY: $VALUE"
fi
done <<< "$HEADERS"
echo ""
# Try mpv first (best experience)
if command -v mpv &>/dev/null; then
echo "Playing with mpv..."
MPV_ARGS=(
"$STREAM_URL"
"--title=BEX: $LABEL"
"--force-media-title=$LABEL"
)
if [ -n "$MPV_HEADERS" ]; then
MPV_ARGS+=("--http-header-fields=${MPV_HEADERS}")
fi
# HLS-specific options
if [ "$FORMAT" = "hls" ] || echo "$STREAM_URL" | grep -q "m3u8"; then
MPV_ARGS+=(
"--demuxer-max-bytes=100MiB"
"--demuxer-max-back-bytes=50MiB"
"--cache=yes"
"--cache-secs=30"
)
fi
exec mpv "${MPV_ARGS[@]}"
# Fallback to ffplay
elif command -v ffplay &>/dev/null; then
echo "Playing with ffplay..."
FFPLAY_ARGS=(
-i "$STREAM_URL"
-window_title "BEX: $LABEL"
-loglevel warning
-autoexit
)
if [ -n "$MPV_HEADERS" ]; then
FFPLAY_ARGS+=(-headers "$MPV_HEADERS")
fi
exec ffplay "${FFPLAY_ARGS[@]}"
# Last resort: just print the URL for manual use
else
echo "No player found (mpv or ffplay required)"
echo ""
echo "Manual playback commands:"
echo ""
echo " mpv \"$STREAM_URL\" \\"
if [ -n "$MPV_HEADERS" ]; then
echo " --http-header-fields=\"${MPV_HEADERS}\" \\"
fi
echo " --cache=yes"
echo ""
echo " ffplay -i \"$STREAM_URL\" \\"
if [ -n "$MPV_HEADERS" ]; then
echo " -headers \"${MPV_HEADERS}\""
fi
echo ""
echo " curl -L -H 'User-Agent: Mozilla/5.0' \"$STREAM_URL\" | ffplay -"
exit 1
fi