set fallback

# --- R2 bucket management (vid.moq.dev) ---

# Initialize the bucket (this is mine; make your own)
init:
    bun install
    bun wrangler r2 bucket create video-moq-dev

# Deploy the worker
deploy:
    bun install
    bun wrangler deploy

# Upload a single file to R2. Fragment videos first, e.g. `ffmpeg -i input.mp4 -c copy -f mp4 -movflags cmaf+separate_moof+delay_moov+skip_trailer+frag_every_frame output.mp4`.
upload file:
    bun install
    bun wrangler r2 object put "video-moq-dev/{{ file }}" --file "media/{{ file }}" --remote

# Sync all files in a local directory to R2
sync dir="media":
    #!/usr/bin/env bash
    set -euo pipefail
    for file in "{{ dir }}"/*; do
    	[ -e "$file" ] || continue
    	key=$(basename "$file")
    	echo "Uploading $key..."
    	bun wrangler r2 object put "video-moq-dev/$key" --file "$file" --remote
    done

# List files in the bucket
list:
    bun install
    bun wrangler r2 object list "video-moq-dev"

# --- Publishing ---

# Publish Big Buck Bunny to a relay server.
bbb url='http://localhost:4443' *args:
    just download bbb
    just cmaf bbb "{{ url }}" {{ args }}

# Publish Tears of Steel to a relay server.
tos url='http://localhost:4443' *args:
    just download tos
    just cmaf tos "{{ url }}" {{ args }}

# Publish a video using ffmpeg (CMAF / fMP4) to a relay server.
cmaf name url='http://localhost:4443' *args:
    cargo build --bin moq
    just ffmpeg-cmaf "media/{{ name }}.mp4" | cargo run --bin moq -- {{ args }} --client-connect "{{ url }}" --broadcast "{{ name }}.hang" import fmp4

# Publish a video using ffmpeg (MPEG-TS) to a relay server.
ts name url='http://localhost:4443' *args:
    cargo build --bin moq
    just ffmpeg-ts "media/{{ name }}.mp4" | cargo run --bin moq -- {{ args }} --client-connect "{{ url }}" --broadcast "{{ name }}.hang" import ts

# Publish a video via iroh.
iroh name url prefix="":
    just download "{{ name }}"
    cargo build --bin moq
    just ffmpeg-cmaf "media/{{ name }}.mp4" | cargo run --bin moq -- --iroh-enabled --client-connect "{{ url }}" --broadcast "{{ prefix }}{{ name }}.hang" import fmp4

# Generate and ingest an HLS stream from a video file.
hls name relay="http://localhost:4443":
    #!/usr/bin/env bash
    set -euo pipefail

    just download "{{ name }}"

    INPUT="media/{{ name }}.mp4"
    OUT_DIR="media/{{ name }}"

    rm -rf "$OUT_DIR"
    mkdir -p "$OUT_DIR"

    echo ">>> Generating HLS stream to disk (1280x720 + 256x144)..."

    ffmpeg -hide_banner -loglevel warning -re -stream_loop -1 -i "$INPUT" \
    	-filter_complex "\
    	[0:v]split=2[v0][v1]; \
    	[v0]scale=-2:720[v720]; \
    	[v1]scale=-2:144[v144]" \
    	-map "[v720]" -map "[v144]" -map 0:a:0 \
    	-r 25 -preset veryfast -g 50 -keyint_min 50 -sc_threshold 0 \
    	-c:v:0 libx264 -profile:v:0 high -level:v:0 4.1 -pix_fmt:v:0 yuv420p -tag:v:0 avc1 \
    	-b:v:0 4M -maxrate:v:0 4.4M -bufsize:v:0 8M \
    	-c:v:1 libx264 -profile:v:1 high -level:v:1 4.1 -pix_fmt:v:1 yuv420p -tag:v:1 avc1 \
    	-b:v:1 300k -maxrate:v:1 330k -bufsize:v:1 600k \
    	-c:a aac -b:a 128k \
    	-f hls -hls_time 2 -hls_list_size 6 \
    	-hls_flags independent_segments+delete_segments \
    	-hls_segment_type fmp4 \
    	-master_pl_name master.m3u8 \
    	-var_stream_map "v:0,agroup:audio,name:720 v:1,agroup:audio,name:144 a:0,agroup:audio,name:audio" \
    	-hls_segment_filename "$OUT_DIR/v%v/segment_%09d.m4s" \
    	"$OUT_DIR/v%v/stream.m3u8" &

    FFMPEG_PID=$!

    echo ">>> Waiting for HLS playlist generation..."
    for i in {1..30}; do
    	if [ -f "$OUT_DIR/master.m3u8" ]; then break; fi
    	sleep 0.5
    done

    if [ ! -f "$OUT_DIR/master.m3u8" ]; then
    	kill $FFMPEG_PID 2>/dev/null || true
    	echo "Error: master.m3u8 not generated in time"
    	exit 1
    fi

    echo ">>> Waiting for variant playlists..."
    sleep 2
    for i in {1..20}; do
    	if [ -f "$OUT_DIR/v0/stream.m3u8" ] || [ -f "$OUT_DIR/v720/stream.m3u8" ] || [ -f "$OUT_DIR/v144/stream.m3u8" ] || [ -f "$OUT_DIR/vaudio/stream.m3u8" ]; then
    		break
    	fi
    	sleep 0.5
    done

    CLEANUP_CALLED=false
    cleanup() {
    	if [ "$CLEANUP_CALLED" = "true" ]; then return; fi
    	CLEANUP_CALLED=true
    	echo "Shutting down..."
    	kill $FFMPEG_PID 2>/dev/null || true
    	sleep 0.5
    	kill -9 $FFMPEG_PID 2>/dev/null || true
    }
    trap cleanup SIGINT SIGTERM EXIT

    echo ">>> Importing HLS via moq-cli"
    cargo run --bin moq -- --client-connect "{{ relay }}" --broadcast "{{ name }}.hang" import hls "$OUT_DIR/master.m3u8"
    EXIT_CODE=$?

    cleanup
    exit $EXIT_CODE

# Publish a video using H.264 Annex B format.
h264 name url="http://localhost:4443" *args:
    just download "{{ name }}"
    cargo build --bin moq

    ffmpeg -hide_banner -v quiet \
    	-stream_loop -1 -re \
    	-i "media/{{ name }}.mp4" \
    	-c:v copy -an \
    	-bsf:v h264_mp4toannexb \
    	-f h264 \
    	- | cargo run --bin moq -- {{ args }} --client-connect "{{ url }}" --broadcast "{{ name }}.hang" import avc3

# Publish a video using ffmpeg directly from moq to the localhost.
serve name *args:
    just download "{{ name }}"
    cargo build --bin moq
    just ffmpeg-cmaf "media/{{ name }}.mp4" | cargo run --bin moq -- {{ args }} --server-bind "[::]:4443" --tls-generate "localhost" --broadcast "{{ name }}.hang" import fmp4

# Generate and serve an HLS stream from a video for testing.
serve-hls name port="8000":
    #!/usr/bin/env bash
    set -euo pipefail

    just download "{{ name }}"

    INPUT="media/{{ name }}.mp4"
    OUT_DIR="media/{{ name }}"

    rm -rf "$OUT_DIR"
    mkdir -p "$OUT_DIR"

    echo ">>> Starting HLS stream generation..."
    echo ">>> Master playlist: http://localhost:{{ port }}/master.m3u8"

    cleanup() {
    	echo "Shutting down..."
    	kill $(jobs -p) 2>/dev/null || true
    	exit 0
    }
    trap cleanup SIGINT SIGTERM

    ffmpeg -loglevel warning -re -stream_loop -1 -i "$INPUT" \
    	-map 0:v:0 -map 0:v:0 -map 0:a:0 \
    	-r 25 -preset veryfast -g 50 -keyint_min 50 -sc_threshold 0 \
    	-c:v:0 libx264 -profile:v:0 high -level:v:0 4.1 -pix_fmt:v:0 yuv420p -tag:v:0 avc1 -bsf:v:0 dump_extra -b:v:0 4M -vf:0 "scale=1920:-2" \
    	-c:v:1 libx264 -profile:v:1 high -level:v:1 4.1 -pix_fmt:v:1 yuv420p -tag:v:1 avc1 -bsf:v:1 dump_extra -b:v:1 300k -vf:1 "scale=256:-2" \
    	-c:a aac -b:a 128k \
    	-f hls \
    	-hls_time 2 -hls_list_size 12 \
    	-hls_flags independent_segments+delete_segments \
    	-hls_segment_type fmp4 \
    	-master_pl_name master.m3u8 \
    	-var_stream_map "v:0,agroup:audio v:1,agroup:audio a:0,agroup:audio" \
    	-hls_segment_filename "$OUT_DIR/v%v/segment_%09d.m4s" \
    	"$OUT_DIR/v%v/stream.m3u8" &

    sleep 2
    echo ">>> HTTP server: http://localhost:{{ port }}/"
    cd "$OUT_DIR" && python3 -m http.server {{ port }}

# Publish the clock broadcast.
clock action url="http://localhost:4443" *args:
    @if [ "{{ action }}" != "publish" ] && [ "{{ action }}" != "subscribe" ]; then \
    	echo "Error: action must be 'publish' or 'subscribe', got '{{ action }}'" >&2; \
    	exit 1; \
    fi

    cargo run -p moq-native --example clock -- --client-connect "{{ url }}" --broadcast "clock" {{ args }} {{ action }}

# --- GStreamer ---

# Publish a video using GStreamer to a relay server.
gst name url='http://localhost:4443' *args:
    just download "{{ name }}"
    cargo build -p moq-gst

    GST_PLUGIN_PATH_1_0="${PWD}/../../target/debug${GST_PLUGIN_PATH_1_0:+:$GST_PLUGIN_PATH_1_0}" \
    gst-launch-1.0 -v -e multifilesrc location="media/{{ name }}.mp4" loop=true ! parsebin name=parse \
    	parse. ! queue ! identity sync=true ! mux.sink_0 \
    	parse. ! queue ! identity sync=true ! mux.sink_1 \
    	moqsink name=mux url="{{ url }}" broadcast="{{ name }}.hang" {{ args }}

# --- Private helpers ---

# Download a test video (already fragmented on vid.moq.dev).
[private]
download name:
    @if [ ! -f "media/{{ name }}.mp4" ]; then \
    	mkdir -p media; \
    	curl -fsSL "https://vid.moq.dev/{{ name }}.mp4" -o "media/{{ name }}.mp4"; \
    fi

# Convert an h264 input file to CMAF (fmp4) format to stdout.
[private]
ffmpeg-cmaf input output='-' *args:
    ffmpeg -hide_banner -v quiet \
    	-stream_loop -1 -re \
    	-i "{{ input }}" \
    	-c copy \
    	-f mp4 -movflags cmaf+separate_moof+delay_moov+skip_trailer+frag_every_frame {{ args }} {{ output }}

# Convert an h264 input file to MPEG-TS format to stdout, with one PES per audio frame.
[private]
ffmpeg-ts input output='-' *args:
    ffmpeg -hide_banner -v quiet \
    	-stream_loop -1 -re \
    	-i "{{ input }}" \
    	-c copy \
    	-f mpegts -pes_payload_size 0 {{ args }} {{ output }}
