Aiwg Cover Art Embedding
Patterns for finding, processing, and embedding cover artwork into media files
git clone https://github.com/jmagly/aiwg
T=$(mktemp -d) && git clone --depth=1 https://github.com/jmagly/aiwg "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.agents/skills/Cover Art Embedding" ~/.claude/skills/jmagly-aiwg-cover-art-embedding && rm -rf "$T"
.agents/skills/Cover Art Embedding/SKILL.mdCover Art Embedding Skill
Concrete patterns for finding high-quality cover artwork, processing it for optimal size and format, and embedding it into audio and video files.
Artwork Sources
MusicBrainz Cover Art Archive (Primary)
MusicBrainz Cover Art Archive (CAA) is the authoritative source for album artwork.
Get Front Cover by Release MBID:
# Download front cover for a release curl -s "https://coverartarchive.org/release/{mbid}/front" -o cover.jpg # Example with real MBID curl -s "https://coverartarchive.org/release/5c004fe3-6e96-3f43-9a85-16e5be8805fa/front" -o blue-cover.jpg
Check if Cover Exists:
# HEAD request to check without downloading if curl -s -I "https://coverartarchive.org/release/${mbid}/front" | grep -q "200 OK"; then echo "Cover available" else echo "No cover found" fi
Get All Available Images:
# Get metadata for all images curl -s "https://coverartarchive.org/release/${mbid}" | jq . # Extract URLs for all front covers curl -s "https://coverartarchive.org/release/${mbid}" | \ jq -r '.images[] | select(.front == true) | .image' # Download highest resolution front cover url=$(curl -s "https://coverartarchive.org/release/${mbid}" | \ jq -r '.images[] | select(.front == true) | .image' | head -1) curl -s "$url" -o cover.jpg
fanart.tv API
fanart.tv provides high-quality artist images, album covers, and logos.
Get Artist Images:
# Requires API key from https://fanart.tv/get-an-api-key/ FANART_API_KEY="your-api-key" # Get all artist artwork curl -s "https://webservice.fanart.tv/v3/music/${mbid}?api_key=${FANART_API_KEY}" # Extract album cover URLs curl -s "https://webservice.fanart.tv/v3/music/${mbid}?api_key=${FANART_API_KEY}" | \ jq -r '.albums."'"$album_mbid"'".albumcover[0].url' # Extract artist background curl -s "https://webservice.fanart.tv/v3/music/${mbid}?api_key=${FANART_API_KEY}" | \ jq -r '.artistbackground[0].url' # Extract artist logo curl -s "https://webservice.fanart.tv/v3/music/${mbid}?api_key=${FANART_API_KEY}" | \ jq -r '.hdmusiclogo[0].url'
Download Artwork:
# Download album cover url=$(curl -s "https://webservice.fanart.tv/v3/music/${mbid}?api_key=${FANART_API_KEY}" | \ jq -r '.albums."'"$album_mbid"'".albumcover[0].url') curl -s "$url" -o cover.jpg
Video Thumbnail Extraction
Extract cover artwork from video frames.
Using yt-dlp (if video was downloaded with thumbnail):
# Download video with thumbnail yt-dlp --write-thumbnail --convert-thumbnails jpg "https://youtube.com/watch?v=..." # Result: video.jpg alongside video.mp4
Using ffmpeg (extract from video):
# Extract single frame at specific time ffmpeg -i input.mp4 -ss 00:00:10 -frames:v 1 -q:v 2 thumbnail.jpg # Extract I-frame (best quality) ffmpeg -i input.mp4 -vf "select=eq(pict_type\,I)" -frames:v 1 -q:v 2 thumbnail.jpg # Extract frame at 25% into video duration=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4) timestamp=$(echo "$duration * 0.25" | bc) ffmpeg -i input.mp4 -ss "$timestamp" -frames:v 1 -q:v 2 thumbnail.jpg # Extract highest quality frame from first minute ffmpeg -i input.mp4 -t 60 -vf "select=eq(pict_type\,I)" -vsync vfr -q:v 1 frames-%03d.jpg # Then manually select best frame
Local Artwork Files
Scan for existing artwork in standard locations:
# Check for common filenames in album directory for name in cover.jpg Cover.jpg folder.jpg Folder.jpg albumart.jpg AlbumArt.jpg; do if [ -f "$name" ]; then echo "Found: $name" break fi done # Search parent directory if [ -f "../cover.jpg" ]; then cp "../cover.jpg" . fi # Find largest image file (likely the cover) largest=$(find . -maxdepth 1 -type f \( -iname "*.jpg" -o -iname "*.png" \) -exec ls -s {} \; | \ sort -rn | head -1 | awk '{print $2}') echo "Largest image: $largest"
Image Processing
Resize for Mobile Optimization
Reduce image size for faster loading and smaller file sizes:
# Install ImageMagick brew install imagemagick # macOS sudo apt install imagemagick # Debian/Ubuntu # Resize to 1200x1200 max (preserves aspect ratio) convert input.jpg -resize 1200x1200 output.jpg # Resize and optimize quality (85% JPEG quality) convert input.jpg -resize 1200x1200 -quality 85 output.jpg # Create thumbnail (300x300) convert input.jpg -resize 300x300 thumbnail.jpg # Batch resize all images in directory for img in *.jpg; do convert "$img" -resize 1200x1200 -quality 85 "${img%.jpg}-resized.jpg" done
Format Conversion
Convert between image formats:
# PNG to JPEG convert input.png output.jpg # WebP to JPEG convert input.webp output.jpg # JPEG to PNG (lossless) convert input.jpg output.png # Batch convert all PNG to JPEG for img in *.png; do convert "$img" "${img%.png}.jpg" done
Crop to Square
Crop non-square images to square aspect ratio:
# Center crop to square convert input.jpg -gravity center -crop 1:1 +repage output.jpg # Crop from top-left convert input.jpg -crop 1200x1200+0+0 +repage output.jpg # Smart crop (removes boring edges) convert input.jpg -define trim:percent-background=0% -trim +repage -resize 1200x1200 output.jpg
Remove Metadata (Privacy)
Strip EXIF data before embedding:
# Remove all metadata convert input.jpg -strip output.jpg # Or use exiftool exiftool -all= input.jpg # Batch strip metadata for img in *.jpg; do convert "$img" -strip "${img%.jpg}-clean.jpg" done
Embedding Artwork
Opus Files (opustags)
# Embed cover art from JPEG file opustags input.opus --set-cover cover.jpg -o output.opus # In-place embedding opustags -i input.opus --set-cover cover.jpg # Verify embedding opustags input.opus | grep -i picture
MP4 Files (ffmpeg)
# Embed cover as attached picture ffmpeg -i input.mp4 -i cover.jpg \ -map 0 -map 1 \ -c copy \ -disposition:v:1 attached_pic \ output.mp4 # Embed with metadata ffmpeg -i input.mp4 -i cover.jpg \ -map 0 -map 1 \ -c copy \ -disposition:v:1 attached_pic \ -metadata:s:v:1 title="Album cover" \ -metadata:s:v:1 comment="Cover (front)" \ output.mp4 # Verify embedding ffprobe -v error -select_streams v -show_entries stream=codec_name,codec_type,disposition input.mp4
MP3 Files (ffmpeg)
# Embed cover with ID3v2.3 ffmpeg -i input.mp3 -i cover.jpg \ -map 0 -map 1 \ -c copy \ -id3v2_version 3 \ -metadata:s:v title="Album cover" \ -metadata:s:v comment="Cover (front)" \ output.mp3 # Verify embedding ffmpeg -i input.mp3 -an -vcodec copy test-extracted-cover.jpg
FLAC Files (ffmpeg)
# Embed cover into FLAC ffmpeg -i input.flac -i cover.jpg \ -map 0 -map 1 \ -c copy \ -disposition:v:0 attached_pic \ output.flac # Using metaflac (alternative) metaflac --import-picture-from=cover.jpg input.flac
Batch Embedding
Embed Same Cover into All Files
# Opus files in directory for file in *.opus; do opustags -i "$file" --set-cover cover.jpg echo "Embedded: $file" done # MP3 files in directory for file in *.mp3; do ffmpeg -i "$file" -i cover.jpg \ -map 0 -map 1 -c copy \ -id3v2_version 3 \ -metadata:s:v title="Album cover" \ "${file%.mp3}-covered.mp3" mv "${file%.mp3}-covered.mp3" "$file" done
Embed Different Covers per Album
# Process album directories for album_dir in */; do # Find cover in album directory cover=$(find "$album_dir" -maxdepth 1 -iname "cover.jpg" | head -1) if [ -n "$cover" ]; then # Embed into all Opus files in this album for file in "$album_dir"/*.opus; do opustags -i "$file" --set-cover "$cover" done echo "Processed: $album_dir" else echo "No cover found: $album_dir" fi done
Download and Embed from MusicBrainz
#!/bin/bash # Process all Opus files, download covers, embed for file in *.opus; do # Extract artist and album from tags artist=$(opustags "$file" | grep "ARTIST=" | cut -d= -f2) album=$(opustags "$file" | grep "ALBUM=" | cut -d= -f2) # Query MusicBrainz for release MBID artist_enc=$(echo "$artist" | jq -sRr @uri) album_enc=$(echo "$album" | jq -sRr @uri) mbdata=$(curl -s "https://musicbrainz.org/ws/2/release/?query=artist:${artist_enc}%20AND%20release:${album_enc}&fmt=json" \ -H "User-Agent: MediaCurator/1.0") mbid=$(echo "$mbdata" | jq -r '.releases[0].id') # Download cover if curl -s "https://coverartarchive.org/release/${mbid}/front" -o "/tmp/cover-${mbid}.jpg"; then # Embed cover opustags -i "$file" --set-cover "/tmp/cover-${mbid}.jpg" echo "Embedded cover: $file" else echo "No cover found: $file" fi # Rate limit sleep 1 done
Canonical Artwork Directory
From field testing: use a single
artwork/ directory for all artwork files, not scattered copies.
Directory Structure
artwork/ ├── albums/ │ ├── blue.jpg │ ├── court-and-spark.jpg │ ├── hejira.jpg │ └── clouds.jpg ├── artists/ │ ├── joni-mitchell-1970.jpg │ ├── joni-mitchell-1976.jpg │ └── joni-mitchell-2000.jpg ├── live/ │ ├── isle-of-wight-1970.jpg │ └── carnegie-hall-1969.jpg ├── logos/ │ └── joni-mitchell-logo.png ├── promotional/ │ ├── blue-era-promo.jpg │ └── court-and-spark-promo.jpg └── ATTRIBUTION.md
Populate Artwork Directory
# Create structure mkdir -p artwork/{albums,artists,live,logos,promotional} # Download album covers cd artwork/albums curl -s "https://coverartarchive.org/release/{mbid}/front" -o blue.jpg curl -s "https://coverartarchive.org/release/{mbid}/front" -o court-and-spark.jpg # Download artist images from fanart.tv cd ../artists curl -s "https://fanart.tv/..." -o joni-mitchell-1970.jpg # Extract video thumbnails cd ../live ffmpeg -i ../../video/isle-of-wight-1970.mp4 -ss 00:01:30 -frames:v 1 -q:v 2 isle-of-wight-1970.jpg
Embed from Canonical Directory
# Reference artwork by path for file in music/Joni\ Mitchell/Blue/*.opus; do opustags -i "$file" --set-cover artwork/albums/blue.jpg done # Or create symlink in album directory ln -s ../../artwork/albums/blue.jpg music/Joni\ Mitchell/Blue/cover.jpg
ATTRIBUTION.md
Record source and rights for all artwork:
# Artwork Attribution ## Albums ### blue.jpg - Source: MusicBrainz Cover Art Archive - URL: https://coverartarchive.org/release/5c004fe3-6e96-3f43-9a85-16e5be8805fa - License: Public Domain - Uploaded by: user123 - Date: 2015-03-20 ### court-and-spark.jpg - Source: fanart.tv - URL: https://fanart.tv/music/... - License: Fair Use - Date: 2018-07-15 ## Artists ### joni-mitchell-1970.jpg - Source: Extracted from "Live at Isle of Wight 1970" video - License: Fair Use - Date: 2026-02-14 ## Live ### isle-of-wight-1970.jpg - Source: Video frame extraction (ffmpeg) - Original video: Live at Isle of Wight 1970.mp4 - License: Fair Use - Date: 2026-02-14
Extract Embedded Artwork
From Opus Files
# opustags does not support extraction, use ffmpeg ffmpeg -i input.opus -an -vcodec copy cover.jpg
From MP4 Files
# Extract first attached picture ffmpeg -i input.mp4 -map 0:v -map -0:V -c copy cover.jpg # Extract all video streams (including artwork) ffmpeg -i input.mp4 -map 0:v -c copy "stream-%d.jpg"
From MP3 Files
# Extract cover art ffmpeg -i input.mp3 -an -vcodec copy cover.jpg # Using id3v2 (alternative) id3v2 -l input.mp3 # List frames id3v2 --APIC input.mp3 > cover.jpg # Extract
From FLAC Files
# Extract using metaflac metaflac --export-picture-to=cover.jpg input.flac # Or ffmpeg ffmpeg -i input.flac -an -vcodec copy cover.jpg
Quality Checks
Verify Artwork is Embedded
# Opus opustags input.opus | grep -i picture # MP4 ffprobe -v error -select_streams v -show_entries stream=disposition:stream_tags input.mp4 # MP3 ffprobe -v error -show_entries format_tags input.mp3 | grep -i picture # Or extract and check file size ffmpeg -i input.mp3 -an -vcodec copy test.jpg ls -lh test.jpg # Should be >50KB for quality cover
Check Image Dimensions
# Using identify (ImageMagick) identify cover.jpg # Using ffprobe ffprobe -v error -select_streams v:0 -show_entries stream=width,height cover.jpg
Verify File Size
# Check cover file size ls -lh cover.jpg # Ideal: 100KB - 500KB # Too small (<50KB): low quality # Too large (>1MB): unnecessary bloat
Troubleshooting
Cover Not Displaying in Media Player
# Check if cover is actually embedded ffprobe -v error -show_entries format_tags input.mp3 # Re-embed with correct disposition ffmpeg -i input.mp4 -i cover.jpg -map 0 -map 1 -c copy \ -disposition:v:1 attached_pic \ output.mp4
Image Format Not Supported
# Convert to JPEG convert input.png output.jpg # Re-embed converted image opustags -i input.opus --set-cover output.jpg
Image Too Large (Slow Loading)
# Resize and optimize convert cover.jpg -resize 1200x1200 -quality 85 cover-optimized.jpg # Re-embed optimized image opustags -i input.opus --set-cover cover-optimized.jpg
MusicBrainz Cover Not Available
# Try fanart.tv curl -s "https://webservice.fanart.tv/v3/music/${mbid}?api_key=${FANART_API_KEY}" | \ jq -r '.albums."'"$album_mbid"'".albumcover[0].url' # Or Google Images (manual) # Or extract from video if available
Complete Workflow Example
#!/bin/bash # Download cover from MusicBrainz, process, and embed file="$1" # Get album MBID from existing tags album=$(opustags "$file" | grep "ALBUM=" | cut -d= -f2) artist=$(opustags "$file" | grep "ARTIST=" | cut -d= -f2) # Query MusicBrainz artist_enc=$(echo "$artist" | jq -sRr @uri) album_enc=$(echo "$album" | jq -sRr @uri) mbdata=$(curl -s "https://musicbrainz.org/ws/2/release/?query=artist:${artist_enc}%20AND%20release:${album_enc}&fmt=json" \ -H "User-Agent: MediaCurator/1.0") mbid=$(echo "$mbdata" | jq -r '.releases[0].id') # Download cover cover_url="https://coverartarchive.org/release/${mbid}/front" curl -s "$cover_url" -o "/tmp/cover-${mbid}.jpg" # Process: resize, optimize, strip metadata convert "/tmp/cover-${mbid}.jpg" \ -resize 1200x1200 \ -quality 85 \ -strip \ "/tmp/cover-${mbid}-processed.jpg" # Embed into file opustags -i "$file" --set-cover "/tmp/cover-${mbid}-processed.jpg" # Save to canonical directory artist_safe=$(echo "$artist" | tr '/' '-') album_safe=$(echo "$album" | tr '/' '-') mkdir -p "artwork/albums" cp "/tmp/cover-${mbid}-processed.jpg" "artwork/albums/${artist_safe}-${album_safe}.jpg" # Update ATTRIBUTION.md cat >> artwork/ATTRIBUTION.md <<EOF ### ${artist_safe}-${album_safe}.jpg - Source: MusicBrainz Cover Art Archive - URL: ${cover_url} - MBID: ${mbid} - Date: $(date +%Y-%m-%d) EOF echo "Cover embedded: $file" echo "Saved to: artwork/albums/${artist_safe}-${album_safe}.jpg"
See Also
- Command:
/tag-collection - Skill:
@metadata-tagging - Agent:
@Metadata Curator - MusicBrainz CAA: https://coverartarchive.org/
- fanart.tv API: https://fanart.tv/api-docs/
- ImageMagick documentation: https://imagemagick.org/
References
- @$AIWG_ROOT/agentic/code/addons/aiwg-utils/rules/human-authorization.md — Seek explicit authorization before overwriting existing embedded artwork
- @$AIWG_ROOT/agentic/code/frameworks/media-curator/skills/metadata-tagging/SKILL.md — Metadata tagging skill used alongside cover art embedding for complete file tagging
- @$AIWG_ROOT/agentic/code/frameworks/media-curator/skills/tag-collection/SKILL.md — Tag collection skill that orchestrates metadata and artwork embedding together
- @$AIWG_ROOT/agentic/code/frameworks/media-curator/skills/integrity-verification/SKILL.md — Verify file integrity after embedding artwork