Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions docs/public/videos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,53 @@ import Video from '@components/Video.astro';
- Consider adding poster images (thumbnails) for better UX
- Compress videos appropriately for web use

## Generating Poster Images

Poster images (video thumbnails) provide a better user experience by showing a preview frame before the video loads. Poster images are automatically detected by the Video component - they should be placed alongside video files with the same name but a `.png` extension.

For example:
- `create-workflow.mp4` → `create-workflow.png` (poster)
- `demo.mp4` → `demo.png` (poster)

To generate poster images for all videos in this directory:

```bash
# From the repository root
./scripts/generate-video-posters.sh
```

This script will:
- Extract a frame at 1 second from each MP4 video
- Generate high-quality PNG poster images (1920x1080)
- Save them alongside the video files with `.png` extension

The Video component automatically detects and uses these poster images:

```mdx
<Video
src="/gh-aw/videos/demo.mp4"
caption="Demo video"
/>
```

No need to specify the `thumbnail` prop - it's automatically derived from the video path!
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation states "No need to specify the thumbnail prop - it's automatically derived from the video path!" However, existing Video component usages in the codebase (e.g., in docs/src/content/docs/setup/creating-workflows.mdx and docs/src/content/docs/setup/quick-start.mdx) still have explicit thumbnail props pointing to non-existent paths like "/gh-aw/images/create-workfow-github-thumbnail.png" and "/gh-aw/images/install-and-add-workflow-in-cli-thumbnail.png".

This creates a discrepancy between the documented behavior and actual usage. These files should be updated to either:

  1. Remove the thumbnail props to use auto-detection
  2. Update the paths to point to the new poster locations

Note: The PR description claims "Removed explicit thumbnail props from all Video component usages" but this hasn't been done.

Suggested change
No need to specify the `thumbnail` prop - it's automatically derived from the video path!
You usually don't need to specify the `thumbnail` prop it's automatically derived from the video path, but you can still override it manually if needed.

Copilot uses AI. Check for mistakes.

## Example

To add a new video to the documentation:

1. Place the video file in this directory: `docs/public/videos/demo.mp4`
2. Reference it in your MDX file:
2. Generate the poster image: `./scripts/generate-video-posters.sh`
3. Reference it in your MDX file:

```mdx
import Video from '@components/Video.astro';

<Video
src="/gh-aw/videos/demo.mp4"
caption="Workflow Demo"
thumbnail="/gh-aw/images/demo-thumbnail.png"
aspectRatio="16:9"
/>
```

The poster image (`demo.png`) will be automatically detected and used.
Binary file added docs/public/videos/create-workflow-on-github.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion docs/src/components/Video.astro
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ const videoFormats: Record<string, string> = {
}
const contentType = videoFormats[fileExtension] || 'video/mp4'

// Auto-detect poster image: replace video extension with .png
// If no explicit thumbnail is provided, assume poster exists with same name
const posterPath = thumbnail || src.replace(/\.[^.]+$/, '.png')
Comment on lines +40 to +42
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regex pattern /\.[^.]+$/ assumes that file extensions are the last dot-separated segment. This works for typical video files, but could produce unexpected results for edge cases like:

  • Files with no extension: "video" → "video.png"
  • Files with multiple dots: "my.video.clip.mp4" → "my.video.clip.png" (works correctly)
  • Files with dots in unusual places

While the current pattern handles most common cases correctly, consider adding validation or documentation about expected file naming conventions.

Suggested change
// Auto-detect poster image: replace video extension with .png
// If no explicit thumbnail is provided, assume poster exists with same name
const posterPath = thumbnail || src.replace(/\.[^.]+$/, '.png')
// Auto-detect poster image by swapping the video extension for .png
// If no explicit thumbnail is provided, assume poster exists with same name
const posterPath = thumbnail || (fileExtension
? `${src.slice(0, -fileExtension.length - 1)}.png`
: `${src}.png`)

Copilot uses AI. Check for mistakes.

// Calculate aspect ratio padding
const ratioMap: Record<string, string> = {
'16:9': '56.25%',
Expand All @@ -56,7 +60,7 @@ const paddingBottom = ratioMap[aspectRatio]
autoplay={autoStart}
loop={repeat}
muted={silenced}
poster={thumbnail}
poster={posterPath}
preload="metadata"
aria-label={caption}
>
Expand Down
61 changes: 61 additions & 0 deletions scripts/generate-video-posters.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
set -e

# Script to generate web-optimized poster images from videos in docs/public/videos
# Posters are extracted at 1 second into the video and saved as PNG files
# alongside the video files (same directory, with .png extension)

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
VIDEOS_DIR="$REPO_ROOT/docs/public/videos"

# Check if ffmpeg is installed
if ! command -v ffmpeg &> /dev/null; then
echo "Error: ffmpeg is not installed. Please install it first:"
echo " Ubuntu/Debian: sudo apt-get install ffmpeg"
echo " macOS: brew install ffmpeg"
exit 1
fi

echo "Generating poster images from videos in $VIDEOS_DIR"
echo "Posters will be saved alongside video files with .png extension"
echo ""

# Process each MP4 video file
for video in "$VIDEOS_DIR"/*.mp4; do
if [ ! -f "$video" ]; then
echo "No video files found in $VIDEOS_DIR"
exit 1
fi

Comment on lines +24 to +30
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script exits with an error when no MP4 files are found. However, this check happens inside the for loop, which means it will only trigger if the glob pattern doesn't match any files. This creates confusing behavior - the error message "No video files found" will be shown even for the first iteration of the loop, which is misleading.

Consider moving this check outside the loop to validate the directory has videos before processing, or handle the empty case more gracefully.

Suggested change
# Process each MP4 video file
for video in "$VIDEOS_DIR"/*.mp4; do
if [ ! -f "$video" ]; then
echo "No video files found in $VIDEOS_DIR"
exit 1
fi
# Collect MP4 video files and ensure there is at least one to process
shopt -s nullglob
mp4_files=("$VIDEOS_DIR"/*.mp4)
if [ ${#mp4_files[@]} -eq 0 ]; then
echo "No video files found in $VIDEOS_DIR"
exit 1
fi
# Process each MP4 video file
for video in "${mp4_files[@]}"; do

Copilot uses AI. Check for mistakes.
# Get the base filename without extension
basename=$(basename "$video" .mp4)

# Generate the output poster filename (PNG in same directory as video)
poster="$VIDEOS_DIR/${basename}.png"

echo "Processing: $basename.mp4"
Comment on lines +32 to +37
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable name "basename" shadows the bash built-in command of the same name. While this works, it's generally a best practice to avoid using reserved words or built-in command names as variable names to prevent confusion and potential issues.

Consider renaming to something like "video_basename" or "video_name" for clarity.

Suggested change
basename=$(basename "$video" .mp4)
# Generate the output poster filename (PNG in same directory as video)
poster="$VIDEOS_DIR/${basename}.png"
echo "Processing: $basename.mp4"
video_basename=$(basename "$video" .mp4)
# Generate the output poster filename (PNG in same directory as video)
poster="$VIDEOS_DIR/${video_basename}.png"
echo "Processing: $video_basename.mp4"

Copilot uses AI. Check for mistakes.
echo " → Extracting frame at 1 second..."

# Extract frame at 1 second, scale to maintain quality
# -ss 1: Seek to 1 second
# -i: Input file
# -vframes 1: Extract only 1 frame
# -q:v 2: High quality (1-31 scale, 2 is very high quality)
# -vf scale: Ensure output is proper size
ffmpeg -ss 1 -i "$video" -vframes 1 -q:v 2 -vf "scale=1920:1080:force_original_aspect_ratio=decrease" "$poster" -y 2>&1 | grep -v "frame=" || true
Comment on lines +40 to +46
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ffmpeg command uses a fixed scale filter that forces output to 1920x1080 with "force_original_aspect_ratio=decrease". This could result in letterboxing or pillarboxing if the source video has a different aspect ratio.

For videos that aren't 16:9 aspect ratio, this will add black bars to fit within 1920x1080. Consider whether this is the desired behavior, or if you want to preserve the original aspect ratio without forced scaling. Alternatively, you could extract the frame at its native resolution to avoid any quality loss or aspect ratio issues.

Suggested change
# Extract frame at 1 second, scale to maintain quality
# -ss 1: Seek to 1 second
# -i: Input file
# -vframes 1: Extract only 1 frame
# -q:v 2: High quality (1-31 scale, 2 is very high quality)
# -vf scale: Ensure output is proper size
ffmpeg -ss 1 -i "$video" -vframes 1 -q:v 2 -vf "scale=1920:1080:force_original_aspect_ratio=decrease" "$poster" -y 2>&1 | grep -v "frame=" || true
# Extract frame at 1 second
# -ss 1: Seek to 1 second
# -i: Input file
# -vframes 1: Extract only 1 frame
# -q:v 2: High quality (1-31 scale, 2 is very high quality)
ffmpeg -ss 1 -i "$video" -vframes 1 -q:v 2 "$poster" -y 2>&1 | grep -v "frame=" || true

Copilot uses AI. Check for mistakes.

if [ -f "$poster" ]; then
size=$(du -h "$poster" | cut -f1)
echo " ✓ Generated: $(basename "$poster") ($size)"
else
echo " ✗ Failed to generate poster"
exit 1
fi
echo ""
done

echo "✓ All poster images generated successfully!"
echo ""
echo "Generated files:"
ls -lh "$VIDEOS_DIR"/*.png 2>/dev/null || true
Loading