- Replace single-file 8441-line HTML with Vue 3 SPA - Pinia stores: auth, oils, recipes, diary, ui - Composables: useApi, useDialog, useSmartPaste, useOilTranslation - 6 shared components: RecipeCard, RecipeDetailOverlay, TagPicker, etc. - 9 page views: RecipeSearch, RecipeManager, Inventory, OilReference, etc. - 14 Cypress E2E test specs (113 tests), all passing - Multi-stage Dockerfile (Node build + Python runtime) - Demo video generation scripts (TTS + subtitles + screen recording) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
68 lines
2.4 KiB
Bash
68 lines
2.4 KiB
Bash
#!/bin/bash
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
FRONTEND_DIR="$(dirname "$SCRIPT_DIR")"
|
|
PROJECT_DIR="$(dirname "$FRONTEND_DIR")"
|
|
OUTPUT_DIR="$FRONTEND_DIR/demo-output"
|
|
|
|
mkdir -p "$OUTPUT_DIR"
|
|
|
|
echo "=== Step 1: Generate TTS audio ==="
|
|
source "$PROJECT_DIR/.venv/bin/activate"
|
|
python3 "$SCRIPT_DIR/generate-tts.py" "$OUTPUT_DIR"
|
|
|
|
echo "=== Step 2: Record Cypress demo ==="
|
|
cd "$FRONTEND_DIR"
|
|
npx cypress run --spec "cypress/e2e/demo-walkthrough.cy.js" || true
|
|
|
|
# Find the recorded video
|
|
VIDEO=$(find cypress/videos -name "demo-walkthrough*" -type f 2>/dev/null | head -1)
|
|
if [ -z "$VIDEO" ]; then
|
|
echo "ERROR: No video found in cypress/videos/"
|
|
exit 1
|
|
fi
|
|
echo "Found video: $VIDEO"
|
|
cp "$VIDEO" "$OUTPUT_DIR/raw-screen.mp4"
|
|
|
|
echo "=== Step 3: Combine video + audio + subtitles ==="
|
|
|
|
# Get video and audio durations
|
|
VIDEO_DUR=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$OUTPUT_DIR/raw-screen.mp4" | cut -d. -f1)
|
|
AUDIO_DUR=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$OUTPUT_DIR/narration.mp3" | cut -d. -f1)
|
|
echo "Video: ${VIDEO_DUR}s, Audio: ${AUDIO_DUR}s"
|
|
|
|
# Use the longer duration, speed-adjust video if needed
|
|
if [ "$VIDEO_DUR" -gt "$AUDIO_DUR" ]; then
|
|
# Speed up video to match audio length
|
|
SPEED=$(python3 -c "print(round($VIDEO_DUR / $AUDIO_DUR, 3))")
|
|
echo "Speeding up video by ${SPEED}x"
|
|
ffmpeg -y -i "$OUTPUT_DIR/raw-screen.mp4" \
|
|
-filter:v "setpts=PTS/${SPEED}" \
|
|
-an "$OUTPUT_DIR/adjusted-screen.mp4" 2>/dev/null
|
|
elif [ "$AUDIO_DUR" -gt "$VIDEO_DUR" ]; then
|
|
# Slow down video
|
|
SPEED=$(python3 -c "print(round($VIDEO_DUR / $AUDIO_DUR, 3))")
|
|
echo "Slowing video to ${SPEED}x"
|
|
ffmpeg -y -i "$OUTPUT_DIR/raw-screen.mp4" \
|
|
-filter:v "setpts=PTS/${SPEED}" \
|
|
-an "$OUTPUT_DIR/adjusted-screen.mp4" 2>/dev/null
|
|
else
|
|
cp "$OUTPUT_DIR/raw-screen.mp4" "$OUTPUT_DIR/adjusted-screen.mp4"
|
|
fi
|
|
|
|
# Combine: video + audio + burned-in subtitles
|
|
ffmpeg -y \
|
|
-i "$OUTPUT_DIR/adjusted-screen.mp4" \
|
|
-i "$OUTPUT_DIR/narration.mp3" \
|
|
-vf "subtitles=$SCRIPT_DIR/demo-subtitles.srt:force_style='FontSize=20,FontName=Noto Sans CJK SC,PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,Outline=2,Shadow=1,MarginV=30'" \
|
|
-c:v libx264 -preset fast -crf 23 \
|
|
-c:a aac -b:a 128k \
|
|
-shortest \
|
|
"$OUTPUT_DIR/demo-final.mp4" 2>/dev/null
|
|
|
|
echo ""
|
|
echo "=== Done! ==="
|
|
echo "Output: $OUTPUT_DIR/demo-final.mp4"
|
|
ls -lh "$OUTPUT_DIR/demo-final.mp4"
|