← back to Goodquestion Ai
scripts/make-video-collection-intelligence.sh
222 lines
#!/bin/bash
# =============================================================================
# Video: Collection Intelligence — From 2,000 to 500 Smart Collections
# Narrated slideshow about AI-driven collection consolidation
# =============================================================================
set -e
BASEDIR="/root/Projects/goodquestion-ai/videos/build-collection-intelligence"
VOICE="en-US-AndrewNeural"
RATE="-10%"
FPS=30
WIDTH=1920
HEIGHT=1080
mkdir -p "$BASEDIR/narration" "$BASEDIR/slides" "$BASEDIR/segments"
echo "═══════════════════════════════════════════════"
echo " STEP 1: Generate Narration (edge-tts)"
echo "═══════════════════════════════════════════════"
# Segment 1: Hook
edge-tts --voice "$VOICE" --rate="$RATE" --text \
"Two thousand collections. Sounds organized, right? It wasn't. Half were duplicates. Hundreds were empty. Naming was completely inconsistent. Customers couldn't navigate the catalog, and search engines were confused. So I built an AI system that audited every single collection, merged the duplicates, culled the dead ones, and rebuilt the taxonomy from scratch. Two thousand down to five hundred. Here's the playbook." \
--write-media "$BASEDIR/narration/01-hook.mp3" 2>/dev/null
echo " 01-hook.mp3"
# Segment 2: The Mess
edge-tts --voice "$VOICE" --rate="$RATE" --text \
"When you have a hundred thousand products across dozens of suppliers, collections multiply like rabbits. Every supplier wants their own collection. Every color gets a collection. Every material, every style, every season. Before you know it, you've got two thousand collections and most of them have fewer than ten products. Some have zero. That's not organization. That's chaos wearing a suit." \
--write-media "$BASEDIR/narration/02-mess.mp3" 2>/dev/null
echo " 02-mess.mp3"
# Segment 3: The Strategy
edge-tts --voice "$VOICE" --rate="$RATE" --text \
"The strategy was five phases. Phase one: audit. Pull every collection, count products, check for empties. Phase two: cull. Delete every collection with zero products. Phase three: merge. Identify duplicates and near-duplicates using fuzzy name matching, then consolidate them. Phase four: rename. Standardize every collection title to a consistent format. Phase five: create tier-one collections. Build the top-level navigation categories that actually make sense for customers." \
--write-media "$BASEDIR/narration/03-strategy.mp3" 2>/dev/null
echo " 03-strategy.mp3"
# Segment 4: AI Detection
edge-tts --voice "$VOICE" --rate="$RATE" --text \
"The AI detection was the clever part. The system used string similarity algorithms to find collections that were essentially the same thing named differently. Blue Geometric and Geometric Blue? Same collection. Modern Abstract and Abstract Modern? Merged. It also analyzed product overlap. If two collections shared more than eighty percent of their products, they got flagged for merge. The algorithm caught hundreds of duplicates that a human would have missed." \
--write-media "$BASEDIR/narration/04-detection.mp3" 2>/dev/null
echo " 04-detection.mp3"
# Segment 5: The Rebuild
edge-tts --voice "$VOICE" --rate="$RATE" --text \
"After the cleanup, I rebuilt the navigation with AI-written collection descriptions. Every collection got a fresh SEO-optimized description, a cover image, and proper sorting rules. The tier-one collections, things like Browse by Color, Browse by Style, Browse by Material, became the backbone of the site navigation. Each one powered by smart rules that automatically pull in new products as they're added to the catalog." \
--write-media "$BASEDIR/narration/05-rebuild.mp3" 2>/dev/null
echo " 05-rebuild.mp3"
# Segment 6: Results
edge-tts --voice "$VOICE" --rate="$RATE" --text \
"The numbers tell the story. Two thousand collections reduced to five hundred. Fifteen hundred dead or duplicate collections removed. Average products per collection went from twelve to forty-eight. Site navigation went from seven clicks deep to three. And the best part? Collection descriptions are now AI-generated, consistent, and optimized for search. The whole process took one weekend." \
--write-media "$BASEDIR/narration/06-results.mp3" 2>/dev/null
echo " 06-results.mp3"
# Segment 7: CTA
edge-tts --voice "$VOICE" --rate="$RATE" --text \
"Collection intelligence isn't just about deleting empties. It's about building a taxonomy that scales. Every new product automatically lands in the right collections. Every collection serves a purpose. If your e-commerce catalog has more collections than it should, you don't need more people. You need smarter systems. Follow Agent Abrams for the next build." \
--write-media "$BASEDIR/narration/07-cta.mp3" 2>/dev/null
echo " 07-cta.mp3"
echo ""
echo "═══════════════════════════════════════════════"
echo " STEP 2: Create Graphic Slides (ffmpeg)"
echo "═══════════════════════════════════════════════"
# Color scheme
BG="#0E0E10"
CYAN="#00F0FF"
ORANGE="#F7931A"
GREEN="#00C389"
WHITE="#E0E0E0"
DIM="#6B7280"
RED="#EF4444"
PURPLE="#A855F7"
FONT="/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
FONTL="/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
# Slide 1: Hook
ffmpeg -y -f lavfi -i "color=c=${BG}:s=${WIDTH}x${HEIGHT}:d=1" \
-vf "drawtext=text='2,000':fontsize=140:fontcolor=${RED}:x=300:y=(h-text_h)/2-60:fontfile=${FONT},\
drawtext=text='-->':fontsize=80:fontcolor=${DIM}:x=830:y=(h-text_h)/2-40:fontfile=${FONTL},\
drawtext=text='500':fontsize=140:fontcolor=${GREEN}:x=1100:y=(h-text_h)/2-60:fontfile=${FONT},\
drawtext=text='COLLECTION INTELLIGENCE':fontsize=40:fontcolor=${CYAN}:x=(w-text_w)/2:y=(h-text_h)/2+120:fontfile=${FONT}" \
-frames:v 1 "$BASEDIR/slides/01-hook.png" 2>/dev/null
echo " 01-hook.png"
# Slide 2: The Mess
ffmpeg -y -f lavfi -i "color=c=${BG}:s=${WIDTH}x${HEIGHT}:d=1" \
-vf "drawtext=text='THE COLLECTION MESS':fontsize=64:fontcolor=${RED}:x=(w-text_w)/2:y=120:fontfile=${FONT},\
drawtext=text='━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━':fontsize=20:fontcolor=${RED}@0.3:x=(w-text_w)/2:y=210:fontfile=${FONTL},\
drawtext=text='▸ 50%% Duplicates':fontsize=40:fontcolor=${WHITE}:x=300:y=300:fontfile=${FONTL},\
drawtext=text='▸ Hundreds of Empty Collections':fontsize=40:fontcolor=${WHITE}:x=300:y=380:fontfile=${FONTL},\
drawtext=text='▸ Inconsistent Naming':fontsize=40:fontcolor=${WHITE}:x=300:y=460:fontfile=${FONTL},\
drawtext=text='▸ 7+ Clicks to Find Products':fontsize=40:fontcolor=${WHITE}:x=300:y=540:fontfile=${FONTL},\
drawtext=text='Chaos wearing a suit':fontsize=36:fontcolor=${ORANGE}:x=(w-text_w)/2:y=680:fontfile=${FONT}" \
-frames:v 1 "$BASEDIR/slides/02-mess.png" 2>/dev/null
echo " 02-mess.png"
# Slide 3: Strategy - 5 Phases
ffmpeg -y -f lavfi -i "color=c=${BG}:s=${WIDTH}x${HEIGHT}:d=1" \
-vf "drawtext=text='5-PHASE STRATEGY':fontsize=64:fontcolor=${CYAN}:x=(w-text_w)/2:y=80:fontfile=${FONT},\
drawtext=text='━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━':fontsize=20:fontcolor=${CYAN}@0.3:x=(w-text_w)/2:y=170:fontfile=${FONTL},\
drawtext=text='1 AUDIT':fontsize=44:fontcolor=${CYAN}:x=250:y=260:fontfile=${FONT},\
drawtext=text='Count products, find empties':fontsize=28:fontcolor=${DIM}:x=550:y=270:fontfile=${FONTL},\
drawtext=text='2 CULL':fontsize=44:fontcolor=${RED}:x=250:y=360:fontfile=${FONT},\
drawtext=text='Delete zero-product collections':fontsize=28:fontcolor=${DIM}:x=550:y=370:fontfile=${FONTL},\
drawtext=text='3 MERGE':fontsize=44:fontcolor=${ORANGE}:x=250:y=460:fontfile=${FONT},\
drawtext=text='Fuzzy match + consolidate':fontsize=28:fontcolor=${DIM}:x=550:y=470:fontfile=${FONTL},\
drawtext=text='4 RENAME':fontsize=44:fontcolor=${GREEN}:x=250:y=560:fontfile=${FONT},\
drawtext=text='Standardize all titles':fontsize=28:fontcolor=${DIM}:x=550:y=570:fontfile=${FONTL},\
drawtext=text='5 BUILD':fontsize=44:fontcolor=${PURPLE}:x=250:y=660:fontfile=${FONT},\
drawtext=text='Create tier-1 navigation':fontsize=28:fontcolor=${DIM}:x=550:y=670:fontfile=${FONTL}" \
-frames:v 1 "$BASEDIR/slides/03-strategy.png" 2>/dev/null
echo " 03-strategy.png"
# Slide 4: AI Detection
ffmpeg -y -f lavfi -i "color=c=${BG}:s=${WIDTH}x${HEIGHT}:d=1" \
-vf "drawtext=text='AI DUPLICATE DETECTION':fontsize=60:fontcolor=${ORANGE}:x=(w-text_w)/2:y=120:fontfile=${FONT},\
drawtext=text='━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━':fontsize=20:fontcolor=${ORANGE}@0.3:x=(w-text_w)/2:y=210:fontfile=${FONTL},\
drawtext=text='String Similarity':fontsize=40:fontcolor=${CYAN}:x=250:y=300:fontfile=${FONT},\
drawtext=text='Blue Geometric = Geometric Blue':fontsize=30:fontcolor=${DIM}:x=250:y=360:fontfile=${FONTL},\
drawtext=text='Product Overlap Analysis':fontsize=40:fontcolor=${GREEN}:x=250:y=460:fontfile=${FONT},\
drawtext=text='80%%+ shared products = merge':fontsize=30:fontcolor=${DIM}:x=250:y=520:fontfile=${FONTL},\
drawtext=text='Hundreds of duplicates caught':fontsize=44:fontcolor=${WHITE}:x=(w-text_w)/2:y=650:fontfile=${FONT}" \
-frames:v 1 "$BASEDIR/slides/04-detection.png" 2>/dev/null
echo " 04-detection.png"
# Slide 5: The Rebuild
ffmpeg -y -f lavfi -i "color=c=${BG}:s=${WIDTH}x${HEIGHT}:d=1" \
-vf "drawtext=text='THE REBUILD':fontsize=64:fontcolor=${GREEN}:x=(w-text_w)/2:y=120:fontfile=${FONT},\
drawtext=text='━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━':fontsize=20:fontcolor=${GREEN}@0.3:x=(w-text_w)/2:y=210:fontfile=${FONTL},\
drawtext=text='▸ AI-Written Descriptions':fontsize=40:fontcolor=${WHITE}:x=300:y=320:fontfile=${FONTL},\
drawtext=text='▸ SEO-Optimized Titles':fontsize=40:fontcolor=${WHITE}:x=300:y=400:fontfile=${FONTL},\
drawtext=text='▸ Cover Images for Every Collection':fontsize=40:fontcolor=${WHITE}:x=300:y=480:fontfile=${FONTL},\
drawtext=text='▸ Smart Rules Auto-Sort Products':fontsize=40:fontcolor=${WHITE}:x=300:y=560:fontfile=${FONTL},\
drawtext=text='Browse by Color | Style | Material':fontsize=44:fontcolor=${CYAN}:x=(w-text_w)/2:y=700:fontfile=${FONT}" \
-frames:v 1 "$BASEDIR/slides/05-rebuild.png" 2>/dev/null
echo " 05-rebuild.png"
# Slide 6: Results
ffmpeg -y -f lavfi -i "color=c=${BG}:s=${WIDTH}x${HEIGHT}:d=1" \
-vf "drawtext=text='THE RESULTS':fontsize=64:fontcolor=${GREEN}:x=(w-text_w)/2:y=80:fontfile=${FONT},\
drawtext=text='━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━':fontsize=20:fontcolor=${GREEN}@0.3:x=(w-text_w)/2:y=170:fontfile=${FONTL},\
drawtext=text='1,500':fontsize=90:fontcolor=${RED}:x=200:y=270:fontfile=${FONT},\
drawtext=text='Dead Collections':fontsize=26:fontcolor=${DIM}:x=200:y=380:fontfile=${FONTL},\
drawtext=text='Removed':fontsize=26:fontcolor=${RED}:x=200:y=415:fontfile=${FONTL},\
drawtext=text='48':fontsize=90:fontcolor=${GREEN}:x=700:y=270:fontfile=${FONT},\
drawtext=text='Avg Products':fontsize=26:fontcolor=${DIM}:x=700:y=380:fontfile=${FONTL},\
drawtext=text='Per Collection':fontsize=26:fontcolor=${GREEN}:x=700:y=415:fontfile=${FONTL},\
drawtext=text='3':fontsize=90:fontcolor=${CYAN}:x=1200:y=270:fontfile=${FONT},\
drawtext=text='Clicks to':fontsize=26:fontcolor=${DIM}:x=1200:y=380:fontfile=${FONTL},\
drawtext=text='Any Product':fontsize=26:fontcolor=${CYAN}:x=1200:y=415:fontfile=${FONTL},\
drawtext=text='One Weekend. Fully Automated.':fontsize=44:fontcolor=${ORANGE}:x=(w-text_w)/2:y=550:fontfile=${FONT}" \
-frames:v 1 "$BASEDIR/slides/06-results.png" 2>/dev/null
echo " 06-results.png"
# Slide 7: CTA
ffmpeg -y -f lavfi -i "color=c=${BG}:s=${WIDTH}x${HEIGHT}:d=1" \
-vf "drawtext=text='AGENT ABRAMS':fontsize=80:fontcolor=${CYAN}:x=(w-text_w)/2:y=(h-text_h)/2-120:fontfile=${FONT},\
drawtext=text='Smarter Systems. Not More People.':fontsize=40:fontcolor=${WHITE}:x=(w-text_w)/2:y=(h-text_h)/2:fontfile=${FONTL},\
drawtext=text='goodquestion.ai':fontsize=36:fontcolor=${ORANGE}:x=(w-text_w)/2:y=(h-text_h)/2+80:fontfile=${FONTL},\
drawtext=text='Subscribe for the next build':fontsize=28:fontcolor=${DIM}:x=(w-text_w)/2:y=(h-text_h)/2+160:fontfile=${FONTL}" \
-frames:v 1 "$BASEDIR/slides/07-cta.png" 2>/dev/null
echo " 07-cta.png"
echo ""
echo "═══════════════════════════════════════════════"
echo " STEP 3: Build per-segment videos"
echo "═══════════════════════════════════════════════"
SEGMENTS=("01-hook" "02-mess" "03-strategy" "04-detection" "05-rebuild" "06-results" "07-cta")
for seg in "${SEGMENTS[@]}"; do
narr="$BASEDIR/narration/${seg}.mp3"
slide="$BASEDIR/slides/${seg}.png"
out="$BASEDIR/segments/${seg}.mp4"
dur=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$narr" 2>/dev/null)
ffmpeg -y \
-loop 1 -i "$slide" \
-i "$narr" \
-c:v libx264 -preset fast -crf 23 -pix_fmt yuv420p \
-c:a aac -b:a 128k \
-t "$dur" \
-r $FPS \
-shortest \
"$out" 2>/dev/null
echo " ${seg}.mp4 (${dur}s)"
done
echo ""
echo "═══════════════════════════════════════════════"
echo " STEP 4: Concatenate all segments"
echo "═══════════════════════════════════════════════"
rm -f "$BASEDIR/concat.txt"
for seg in "${SEGMENTS[@]}"; do
echo "file 'segments/${seg}.mp4'" >> "$BASEDIR/concat.txt"
done
FINAL="$BASEDIR/collection-intelligence.mp4"
ffmpeg -y -f concat -safe 0 -i "$BASEDIR/concat.txt" \
-c:v libx264 -preset medium -crf 22 -pix_fmt yuv420p \
-c:a aac -b:a 192k \
-movflags +faststart \
"$FINAL" 2>/dev/null
FILESIZE=$(du -h "$FINAL" | cut -f1)
DURATION=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$FINAL" 2>/dev/null)
echo ""
echo "═══════════════════════════════════════════════"
echo " DONE!"
echo "═══════════════════════════════════════════════"
echo " Output: $FINAL"
echo " Size: $FILESIZE"
echo " Duration: ${DURATION}s"