Files
upstream-mirror a774ac673a
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
2026-09-23 16:12:52 +05:00

90 lines
2.9 KiB
Bash
Executable File

#!/bin/bash
# make-video.sh — Assemble walkthrough slides + audio into a final MP4.
#
# Usage:
# ./make-video.sh <slide-dir> <output.mp4> [outro-duration]
#
# Expects in <slide-dir>:
# slide-00.png, slide-01.png, ... (one per segment, including outro)
# audio-00.wav, audio-01.wav, ... (one per narrated segment)
#
# The last slide PNG without a matching audio WAV is the silent outro.
#
set -euo pipefail
SLIDE_DIR="${1:?Usage: make-video.sh <slide-dir> <output.mp4> [outro-duration]}"
OUTPUT="${2:?Usage: make-video.sh <slide-dir> <output.mp4> [outro-duration]}"
OUTRO_DUR="${3:-3}"
# Resolve relative paths
[[ "$SLIDE_DIR" != /* ]] && SLIDE_DIR="$(pwd)/$SLIDE_DIR"
[[ "$OUTPUT" != /* ]] && OUTPUT="$(pwd)/$OUTPUT"
mkdir -p "$(dirname "$OUTPUT")"
TMPDIR_WORK=$(mktemp -d)
trap "rm -rf $TMPDIR_WORK" EXIT
echo "=== Assembling video ==="
echo " Slides: $SLIDE_DIR"
echo " Output: $OUTPUT"
# Count slides and audio
SLIDE_COUNT=$(ls "$SLIDE_DIR"/slide-*.png 2>/dev/null | wc -l | tr -d ' ')
AUDIO_COUNT=$(ls "$SLIDE_DIR"/audio-*.wav 2>/dev/null | wc -l | tr -d ' ')
echo " Found $SLIDE_COUNT slides, $AUDIO_COUNT audio clips"
echo " Last slide (no audio) = outro (${OUTRO_DUR}s)"
# Create per-segment videos
CONCAT_LIST="$TMPDIR_WORK/concat.txt"
> "$CONCAT_LIST"
for i in $(seq 0 $((SLIDE_COUNT - 1))); do
NUM=$(printf "%02d" $i)
SLIDE="$SLIDE_DIR/slide-${NUM}.png"
AUDIO="$SLIDE_DIR/audio-${NUM}.wav"
SEGMENT="$TMPDIR_WORK/segment-${NUM}.mp4"
if [ -f "$AUDIO" ]; then
# Get audio duration
DUR=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$AUDIO")
echo " segment-${NUM}: slide + audio (${DUR}s)"
# Create video segment: static image for duration of audio, with audio
ffmpeg -y -loop 1 -i "$SLIDE" -i "$AUDIO" \
-c:v libx264 -tune stillimage -pix_fmt yuv420p \
-vf "scale=1600:900:force_original_aspect_ratio=decrease,pad=1600:900:(ow-iw)/2:(oh-ih)/2" \
-c:a aac -b:a 192k -ar 48000 \
-shortest -movflags +faststart \
"$SEGMENT" 2>/dev/null
else
# Silent outro slide
echo " segment-${NUM}: silent outro (${OUTRO_DUR}s)"
ffmpeg -y -loop 1 -i "$SLIDE" -f lavfi -i anullsrc=r=48000:cl=mono \
-c:v libx264 -tune stillimage -pix_fmt yuv420p \
-vf "scale=1600:900:force_original_aspect_ratio=decrease,pad=1600:900:(ow-iw)/2:(oh-ih)/2" \
-c:a aac -b:a 192k -ar 48000 \
-t "$OUTRO_DUR" -movflags +faststart \
"$SEGMENT" 2>/dev/null
fi
echo "file '$SEGMENT'" >> "$CONCAT_LIST"
done
# Concatenate all segments
echo ""
echo " Concatenating ${SLIDE_COUNT} segments..."
ffmpeg -y -f concat -safe 0 -i "$CONCAT_LIST" \
-c copy -movflags +faststart \
"$OUTPUT" 2>/dev/null
# Report
FINAL_DUR=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$OUTPUT")
FINAL_SIZE=$(ls -lh "$OUTPUT" | awk '{print $5}')
echo ""
echo "=== Done ==="
echo " Output: $OUTPUT"
echo " Duration: ${FINAL_DUR}s"
echo " Size: $FINAL_SIZE"