plugins/media-curator/skills/export/SKILL.md
Export media collection to platform-specific formats (Plex, Jellyfin, MPD, mobile, archival)
npx skillsauth add jmagly/aiwg exportInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Export a curated media collection to platform-specific formats with appropriate transcoding, metadata, and directory structures.
Users consume media across diverse platforms, each with different format requirements, metadata standards, and organizational conventions. This command transforms a curated collection into the optimal structure for each target platform.
Formats:
folder.jpg in album directoryDirectory Structure:
Music/
├── Artist Name/
│ ├── Album Name (Year)/
│ │ ├── folder.jpg
│ │ ├── 01 - Track Title.flac
│ │ ├── 02 - Track Title.flac
│ │ └── ...
│ └── Videos/
│ └── Video Title.mp4
YAML Spec:
profile: plex
audio_formats: [flac, mp3, m4a]
video_formats: [mp4, mkv]
artwork:
embedded: true
folder_jpg: true
max_resolution: 1500x1500
directory_pattern: "{artist}/{album} ({year})/{track:02d} - {title}.{ext}"
video_directory: "{artist}/Videos/{title}.{ext}"
metadata:
prefer_embedded: true
id3v2_version: 2.4
Formats:
folder.jpg + artist.jpgDirectory Structure:
Music/
├── Artist Name/
│ ├── artist.jpg
│ ├── artist.nfo
│ ├── Album Name (Year)/
│ │ ├── folder.jpg
│ │ ├── album.nfo
│ │ ├── 01 - Track Title.flac
│ │ └── ...
│ └── Videos/
│ ├── Video Title.mp4
│ └── Video Title.nfo
YAML Spec:
profile: jellyfin
audio_formats: [flac, opus, mp3]
video_formats: [mp4, mkv, webm]
artwork:
embedded: true
folder_jpg: true
artist_jpg: true
max_resolution: 1500x1500
nfo_files:
album: true
artist: true
video: true
directory_pattern: "{artist}/{album} ({year})/{track:02d} - {title}.{ext}"
video_directory: "{artist}/Videos/{title}.{ext}"
metadata:
prefer_embedded: true
id3v2_version: 2.4
Formats:
folder.jpg in album directoryDirectory Structure:
Music/
├── Artist Name/
│ └── Album Name/
│ ├── folder.jpg
│ ├── 01 - Track Title.flac
│ └── ...
YAML Spec:
profile: mpd
audio_formats: [flac, opus, mp3]
artwork:
embedded: true
folder_jpg: true
max_resolution: 1000x1000
directory_pattern: "{artist}/{album}/{track:02d} - {title}.{ext}"
metadata:
prefer_embedded: true
id3v2_version: 2.4
scanning:
auto_rescan: true
watch_mode: inotify
Formats:
Size Constraints:
YAML Spec:
profile: mobile
audio_formats: [opus, m4a, mp3]
audio_quality:
opus: 128k
m4a: 192k
mp3: 192k
video_formats: [mp4]
video_quality:
max_resolution: 720p
codec: h264
bitrate: 1500k
artwork:
embedded: true
max_resolution: 500x500
transcode:
always: true
source_formats: [flac, wav, alac]
size_budget:
enabled: true
prioritize_by: importance
directory_pattern: "{artist}/{album}/{track:02d} - {title}.{ext}"
Formats:
Directory Structure:
Archive/
├── Artist Name/
│ ├── Album Name (Year)/
│ │ ├── 01 - Track Title.flac
│ │ ├── ...
│ │ ├── SHA256SUMS
│ │ ├── PROVENANCE.jsonld
│ │ └── folder.jpg
│ └── MANIFEST.json
YAML Spec:
profile: archival
audio_formats: [flac, wav]
video_formats: [mkv]
preserve:
original_filenames: true
source_metadata: true
acquisition_dates: true
checksums:
algorithm: sha256
filename: SHA256SUMS
provenance:
format: jsonld
filename: PROVENANCE.jsonld
include_source_urls: true
include_timestamps: true
manifest:
per_album: false
per_artist: true
format: json
directory_pattern: "{artist}/{album} ({year})/{original_filename}"
FLAC to Opus:
ffmpeg -i input.flac -c:a libopus -b:a 128k -vn output.opus
FLAC to MP3:
ffmpeg -i input.flac -c:a libmp3lame -b:a 320k -id3v2_version 4 output.mp3
Opus to MP3:
ffmpeg -i input.opus -c:a libmp3lame -b:a 192k -id3v2_version 4 output.mp3
Preserve Metadata:
ffmpeg -i input.flac -c:a libopus -b:a 128k -vn -map_metadata 0 output.opus
4K to 1080p:
ffmpeg -i input.mkv -vf scale=1920:1080 -c:v libx264 -crf 23 -c:a copy output.mp4
1080p to 720p (mobile):
ffmpeg -i input.mp4 -vf scale=1280:720 -c:v libx264 -crf 23 -b:v 1500k -c:a aac -b:a 128k output.mp4
Extract Audio Only:
ffmpeg -i video.mp4 -vn -c:a libopus -b:a 192k audio.opus
Resize Artwork:
ffmpeg -i folder.jpg -vf scale=500:500:force_original_aspect_ratio=decrease -q:v 2 folder_mobile.jpg
Extract Artwork from Audio:
ffmpeg -i track.flac -an -vcodec copy folder.jpg
Embed Artwork into Audio:
ffmpeg -i track.flac -i folder.jpg -map 0:a -map 1:v -c:a copy -id3v2_version 4 -metadata:s:v title="Album cover" -metadata:s:v comment="Cover (front)" output.flac
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<album>
<title>{album_title}</title>
<artist>{artist_name}</artist>
<year>{release_year}</year>
<genre>{genre}</genre>
<style>{style}</style>
<mood>{mood}</mood>
<label>{label}</label>
<releasedate>{release_date}</releasedate>
<rating>{rating}</rating>
<userrating>{user_rating}</userrating>
<review>{review}</review>
<musicbrainzalbumid>{mbid}</musicbrainzalbumid>
<thumb aspect="thumb">{artwork_url}</thumb>
</album>
When exporting to mobile devices with limited storage:
Calculate Space Budget:
--max-size parameterPrioritize Content:
Transcode to Mobile Formats:
Track Running Total:
Export Algorithm:
budget_bytes=$(( max_size_gb * 1024 * 1024 * 1024 * 9 / 10 ))
total_bytes=0
while read -r file importance; do
file_size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file")
if (( total_bytes + file_size > budget_bytes )); then
echo "Budget exhausted. Stopping export." >&2
break
fi
# Transcode and copy
transcode_and_copy "$file" "$output_dir"
total_bytes=$(( total_bytes + file_size ))
done < <(prioritized_file_list)
Never transcode. Copy files as-is to preserve quality and authenticity.
cd "$album_dir"
find . -type f -not -name "SHA256SUMS" -exec sha256sum {} \; > SHA256SUMS
Create PROVENANCE.jsonld per W3C PROV standard:
{
"@context": "https://www.w3.org/ns/prov",
"entity": "urn:aiwg:media:album:{mbid}",
"wasGeneratedBy": {
"activity": "urn:aiwg:activity:acquisition:{timestamp}",
"time": "{iso8601_timestamp}",
"wasAssociatedWith": "urn:aiwg:agent:media-curator"
},
"wasDerivedFrom": [
{
"entity": "{source_url}",
"type": "download",
"time": "{acquisition_date}"
}
],
"atLocation": "{file_path}",
"hadPrimarySource": "{authoritative_source}"
}
Per-artist MANIFEST.json listing all albums, tracks, formats, and checksums:
{
"artist": "Artist Name",
"albums": [
{
"title": "Album Name",
"year": 1973,
"tracks": [
{
"number": 1,
"title": "Track Title",
"file": "01 - Track Title.flac",
"format": "flac",
"checksum": "sha256:abc123..."
}
]
}
],
"export_date": "{iso8601_timestamp}",
"profile": "archival"
}
Parse --profile argument. Load corresponding YAML spec.
Based on profile's directory_pattern:
mkdir -p "$output_dir/{artist}/{album}"
Check if source format matches target profile. If not, transcode:
if [[ "$source_ext" == "flac" ]] && [[ "$target_profile" == "mobile" ]]; then
ffmpeg -i "$source" -c:a libopus -b:a 128k -map_metadata 0 "$target"
fi
cp -a "$source" "$destination"
Or move if --move flag set.
Ensure all required metadata is embedded per profile spec.
Export complete: Plex profile
Source: /media/source/Artist Name
Destination: /media/plex/Music/Artist Name
Files exported: 142
Total size: 3.2 GB
Transcoded: 0
Copied: 142
/export --profile plex ~/Music/Archive/Pink\ Floyd /media/plex/Music
/export --profile mobile ~/Music/Archive /sdcard/Music --max-size 32 --transcode
/export --profile archival ~/Music/Curated /backup/Music/Archive
data-ai
Report which research-corpus radar sidecars are overdue for refresh. Computes staleness (days since last refresh vs the cadence window) for every radar, sorted most-overdue-first. Runs via `aiwg corpus radar-status`.
data-ai
Aggregate research-corpus radar sidecars into a corpus or per-cluster freshness report — totals, overdue count, per-cluster / per-GRADE / per-trajectory breakdowns, an overdue table, and per-radar rationale snippets. Runs via `aiwg corpus radar-report`.
testing
Scaffold radar/freshness sidecars for research-corpus REFs. Pulls title/authors from the citation sidecar and GRADE from the analysis doc, defaults the refresh cadence from GRADE and the cluster from a corpus-local map, and stamps documentation/radar/REF-XXX-radar.md. Runs via `aiwg corpus radar-init`.
data-ai
Compute an entity's publication trajectory — per-year paper counts, topic drift, hot-streak detection (≥3 consecutive A-grade years), and career phase. Runs via `aiwg corpus profile-temporal`.