---
namespace: aiwg
name: export
platforms: [all]
description: Export media collection to platform-specific formats (Plex, Jellyfin, MPD, mobile, archival)
commandHint:
  argumentHint: --profile <plex|jellyfin|mpd|mobile|archival> <collection_path> <output_path> [--max-size <GB>] [--transcode]
  allowedTools: Bash, Read, Write, Glob, Grep
  model: sonnet
  category: media-curator
---

# Export Media Collection

Export a curated media collection to platform-specific formats with appropriate transcoding, metadata, and directory structures.

## Purpose

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.

## Platform Profiles

### Plex Profile

**Formats:**
- Audio: FLAC (preferred), MP3 (320kbps), M4A (AAC)
- Video: MP4 (H.264/H.265), MKV
- Artwork: Embedded + `folder.jpg` in album directory

**Directory Structure:**
```
Music/
├── Artist Name/
│   ├── Album Name (Year)/
│   │   ├── folder.jpg
│   │   ├── 01 - Track Title.flac
│   │   ├── 02 - Track Title.flac
│   │   └── ...
│   └── Videos/
│       └── Video Title.mp4
```

**YAML Spec:**
```yaml
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
```

### Jellyfin Profile

**Formats:**
- Audio: FLAC (preferred), Opus, MP3
- Video: MP4, MKV, WebM
- Artwork: Embedded + `folder.jpg` + `artist.jpg`
- NFO: Supported for rich metadata

**Directory 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:**
```yaml
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
```

### MPD Profile

**Formats:**
- Audio: FLAC (preferred), Opus, MP3
- Artwork: `folder.jpg` in album directory

**Directory Structure:**
```
Music/
├── Artist Name/
│   └── Album Name/
│       ├── folder.jpg
│       ├── 01 - Track Title.flac
│       └── ...
```

**YAML Spec:**
```yaml
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
```

### Mobile Profile

**Formats:**
- Audio: Opus (preferred, 128-192kbps), M4A (AAC), MP3
- Video: MP4 (H.264, max 720p)
- Artwork: Embedded, max 500x500

**Size Constraints:**
- Transcode high-res audio to save space
- Limit video resolution
- Budget-aware export (stop when size limit reached)

**YAML Spec:**
```yaml
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}"
```

### Archival Profile

**Formats:**
- Audio: FLAC, WAV (no lossy formats)
- Video: MKV (preserve original quality)
- Checksums: SHA256SUMS
- Provenance: PROVENANCE.jsonld

**Directory Structure:**
```
Archive/
├── Artist Name/
│   ├── Album Name (Year)/
│   │   ├── 01 - Track Title.flac
│   │   ├── ...
│   │   ├── SHA256SUMS
│   │   ├── PROVENANCE.jsonld
│   │   └── folder.jpg
│   └── MANIFEST.json
```

**YAML Spec:**
```yaml
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}"
```

## Transcoding Commands

### Audio Transcoding

**FLAC to Opus:**
```bash
ffmpeg -i input.flac -c:a libopus -b:a 128k -vn output.opus
```

**FLAC to MP3:**
```bash
ffmpeg -i input.flac -c:a libmp3lame -b:a 320k -id3v2_version 4 output.mp3
```

**Opus to MP3:**
```bash
ffmpeg -i input.opus -c:a libmp3lame -b:a 192k -id3v2_version 4 output.mp3
```

**Preserve Metadata:**
```bash
ffmpeg -i input.flac -c:a libopus -b:a 128k -vn -map_metadata 0 output.opus
```

### Video Transcoding

**4K to 1080p:**
```bash
ffmpeg -i input.mkv -vf scale=1920:1080 -c:v libx264 -crf 23 -c:a copy output.mp4
```

**1080p to 720p (mobile):**
```bash
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:**
```bash
ffmpeg -i video.mp4 -vn -c:a libopus -b:a 192k audio.opus
```

### Artwork Processing

**Resize Artwork:**
```bash
ffmpeg -i folder.jpg -vf scale=500:500:force_original_aspect_ratio=decrease -q:v 2 folder_mobile.jpg
```

**Extract Artwork from Audio:**
```bash
ffmpeg -i track.flac -an -vcodec copy folder.jpg
```

**Embed Artwork into Audio:**
```bash
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
```

## NFO File Generation

### Album NFO Template (Jellyfin/Kodi)

```xml
<?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>
```

## Size-Budgeted Export (Mobile)

When exporting to mobile devices with limited storage:

1. **Calculate Space Budget:**
   - Parse `--max-size` parameter
   - Convert to bytes
   - Reserve 10% buffer for filesystem overhead

2. **Prioritize Content:**
   - Sort by importance score (frequency * rating * recency)
   - Essential albums first
   - Deep cuts last

3. **Transcode to Mobile Formats:**
   - FLAC → Opus 128kbps
   - High-res video → 720p H.264
   - Resize artwork to 500x500

4. **Track Running Total:**
   - Sum file sizes as files are added
   - Stop when budget exhausted
   - Generate report of included/excluded content

5. **Export Algorithm:**
```bash
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)
```

## Archival Export

### Preserve Original Formats

Never transcode. Copy files as-is to preserve quality and authenticity.

### Generate SHA-256 Checksums

```bash
cd "$album_dir"
find . -type f -not -name "SHA256SUMS" -exec sha256sum {} \; > SHA256SUMS
```

### Record Provenance

Create `PROVENANCE.jsonld` per W3C PROV standard:

```json
{
  "@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}"
}
```

### Generate Manifest

Per-artist `MANIFEST.json` listing all albums, tracks, formats, and checksums:

```json
{
  "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"
}
```

## Workflow

### 1. Select Profile

Parse `--profile` argument. Load corresponding YAML spec.

### 2. Validate Source Collection

- Scan collection directory
- Verify files exist and are readable
- Check format compatibility with target profile

### 3. Create Output Directory Structure

Based on profile's `directory_pattern`:

```bash
mkdir -p "$output_dir/{artist}/{album}"
```

### 4. Transcode If Needed

Check if source format matches target profile. If not, transcode:

```bash
if [[ "$source_ext" == "flac" ]] && [[ "$target_profile" == "mobile" ]]; then
  ffmpeg -i "$source" -c:a libopus -b:a 128k -map_metadata 0 "$target"
fi
```

### 5. Copy/Move Files

```bash
cp -a "$source" "$destination"
```

Or move if `--move` flag set.

### 6. Embed Metadata

Ensure all required metadata is embedded per profile spec.

### 7. Generate Supplementary Files

- NFO files (Jellyfin profile)
- SHA256SUMS (archival profile)
- PROVENANCE.jsonld (archival profile)
- folder.jpg, artist.jpg (Plex/Jellyfin profiles)

### 8. Report

```
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
```

## Examples

### Export to Plex

```bash
/export --profile plex ~/Music/Archive/Pink\ Floyd /media/plex/Music
```

### Export to Mobile with Size Limit

```bash
/export --profile mobile ~/Music/Archive /sdcard/Music --max-size 32 --transcode
```

### Archival Export

```bash
/export --profile archival ~/Music/Curated /backup/Music/Archive
```

## Error Handling

- Missing source files: Report and skip
- Insufficient disk space: Abort with clear message
- Transcode failures: Log error, optionally skip or abort
- Permission denied: Report file path and required permissions

## Performance Considerations

- Parallel transcoding: Use GNU parallel or xargs -P for multi-core systems
- Progress reporting: Update every N files or every M seconds
- Disk I/O: Batch operations to minimize seeks
- Memory: Stream large files instead of loading into memory

## References

- @$AIWG_ROOT/agentic/code/addons/aiwg-utils/rules/human-authorization.md — Seek explicit authorization before overwriting existing export targets
- @$AIWG_ROOT/agentic/code/frameworks/media-curator/skills/curate/SKILL.md — Orchestration skill that invokes export as the final curation phase
- @$AIWG_ROOT/agentic/code/frameworks/media-curator/skills/assemble/SKILL.md — Assembly skill used before export to prepare media collections
- @$AIWG_ROOT/agentic/code/frameworks/media-curator/skills/integrity-verification/SKILL.md — Verify exported files match source checksums
