# Song Parser API

> PHP module for reading, modifying, and generating ProPresenter `.pro` song files.

## Quick Reference

```php
use ProPresenter\Parser\ProFileReader;
use ProPresenter\Parser\ProFileWriter;
use ProPresenter\Parser\ProFileGenerator;

// Read
$song = ProFileReader::read('path/to/song.pro');

// Modify
$song->setName("New Name");
ProFileWriter::write($song, 'output.pro');

// Generate
$song = ProFileGenerator::generate('Song Name', $groups, $arrangements, $ccli);
```

---

## Reading Songs

```php
use ProPresenter\Parser\ProFileReader;

$song = ProFileReader::read('path/to/song.pro');
```

### Metadata Access

```php
// Basic info
$song->getName();        // "Amazing Grace"
$song->getUuid();        // "A1B2C3D4-..."

// CCLI metadata
$song->getCcliAuthor();          // "Joel Houston, Matt Crocker"
$song->getCcliSongTitle();       // "Oceans (Where Feet May Fail)"
$song->getCcliPublisher();       // "2012 Hillsong Music Publishing"
$song->getCcliCopyrightYear();   // 2012
$song->getCcliSongNumber();      // 6428767
$song->getCcliDisplay();         // true

// Other metadata
$song->getCategory();                   // ""
$song->getNotes();                      // ""
$song->getSelectedArrangementUuid();    // "uuid-string"
```

---

## Groups

Groups are song parts (Verse 1, Chorus, Bridge, etc.).

```php
foreach ($song->getGroups() as $group) {
    $group->getName();        // "Verse 1"
    $group->getUuid();        // "E5F6G7H8-..."
    $group->getColor();       // ['r' => 1.0, 'g' => 0.0, 'b' => 0.0, 'a' => 1.0] or null
    $group->getSlideUuids();  // ["uuid1", "uuid2", ...]
}

// Get specific group
$chorus = $song->getGroupByName("Chorus");

// Get slides for a group
$slides = $song->getSlidesForGroup($group);
```

---

## Slides

Slides are individual presentation frames.

```php
foreach ($song->getSlides() as $slide) {
    $slide->getUuid();
    $slide->getPlainText();   // Extracted from first text element
    $slide->getLabel();       // Optional cue label/title
}

// Access all text elements
foreach ($slide->getTextElements() as $textElement) {
    $textElement->getName();       // "Orginal", "Deutsch", etc.
    $textElement->getRtfData();    // Raw RTF bytes
    $textElement->getPlainText();  // Extracted plain text
}
```

### Translations

Multiple text elements per slide indicate translations.

```php
if ($slide->hasTranslation()) {
    $translation = $slide->getTranslation();
    $translation->getPlainText();  // Translated text
}
```

### Macros

```php
if ($slide->hasMacro()) {
    $slide->getMacroName();           // "Lied 1.Folie"
    $slide->getMacroUuid();           // "20C1DFDE-..."
    $slide->getMacroCollectionName(); // "--MAIN--"
    $slide->getMacroCollectionUuid(); // "8D02FC57-..."
}
```

### Media

```php
if ($slide->hasMedia()) {
    $slide->getMediaUrl();     // "file:///Users/me/Pictures/slide.jpg"
    $slide->getMediaUuid();    // "uuid-string"
    $slide->getMediaFormat();  // "JPG"
}
```

---

## Arrangements

Arrangements define group order for presentations.

```php
foreach ($song->getArrangements() as $arrangement) {
    $arrangement->getName();       // "normal"
    $arrangement->getGroupUuids(); // ["uuid1", "uuid2", "uuid1", ...] (can repeat)
}

// Resolve groups in arrangement order
$groups = $song->getGroupsForArrangement($arrangement);
foreach ($groups as $group) {
    echo $group->getName();
}
```

---

## Modifying Songs

```php
use ProPresenter\Parser\ProFileWriter;

// Metadata
$song->setName("New Song Title");
$song->setCategory("Worship");
$song->setNotes("Use acoustic intro");

// CCLI
$song->setCcliAuthor("Author Name");
$song->setCcliSongTitle("Song Title");
$song->setCcliPublisher("Publisher");
$song->setCcliCopyrightYear(2024);
$song->setCcliSongNumber(12345);
$song->setCcliDisplay(true);

// Group names
$group = $song->getGroupByName("Verse 1");
$group->setName("Strophe 1");

// Slide labels/macros
$slide = $song->getSlides()[0];
$slide->setLabel('New Label');
$slide->setMacro(
    'Macro Name',
    'macro-uuid',
    '--MAIN--',
    'collection-uuid'
);
$slide->removeMacro();

// Write
ProFileWriter::write($song, 'output.pro');
```

---

## Generating Songs

```php
use ProPresenter\Parser\ProFileGenerator;

$song = ProFileGenerator::generate(
    'Amazing Grace',
    [
        [
            'name' => 'Verse 1',
            'color' => [0.13, 0.59, 0.95, 1.0],  // RGBA floats
            'slides' => [
                ['text' => 'Amazing grace, how sweet the sound'],
                ['text' => 'That saved a wretch like me', 'translation' => 'Der mich Verlornen fand'],
            ],
        ],
        [
            'name' => 'Chorus',
            'color' => [0.95, 0.27, 0.27, 1.0],
            'slides' => [
                ['text' => 'I once was lost, but now am found'],
            ],
        ],
        [
            'name' => 'Media',
            'color' => [0.2, 0.2, 0.2, 1.0],
            'slides' => [
                ['media' => 'file:///Users/me/Pictures/slide.jpg', 'format' => 'JPG', 'label' => 'slide.jpg'],
            ],
        ],
    ],
    [
        ['name' => 'normal', 'groupNames' => ['Verse 1', 'Chorus', 'Verse 1']],
    ],
    [
        'author' => 'John Newton',
        'song_title' => 'Amazing Grace',
        'copyright_year' => 1779,
    ]
);

// Generate and write in one call
ProFileGenerator::generateAndWrite('output.pro', 'Song Name', $groups, $arrangements, $ccli);
```

### Slide Options

```php
// Text only
['text' => 'Lyrics here']

// Text with translation
['text' => 'English lyrics', 'translation' => 'Deutsche Lyrics']

// Text with macro
['text' => 'Lyrics', 'macro' => ['name' => 'Macro Name', 'uuid' => 'macro-uuid']]

// Media slide
['media' => 'file:///path/to/image.jpg', 'format' => 'JPG', 'label' => 'image.jpg']

// Media with macro
['media' => 'file:///path/to/video.mp4', 'format' => 'MP4', 'label' => 'video.mp4', 'macro' => ['name' => 'Macro', 'uuid' => 'uuid']]
```

---

## CLI Tool

```bash
php php/bin/parse-song.php path/to/song.pro
```

Output includes:
- Song metadata (name, UUID, CCLI info)
- Groups with slide counts
- Slides with text content and translations
- Arrangements with group order

---

## Error Handling

```php
try {
    $song = ProFileReader::read('song.pro');
} catch (\RuntimeException $e) {
    // File not found, empty file, or invalid protobuf
    echo "Error: " . $e->getMessage();
}
```

---

## Key Files

| File | Purpose |
|------|---------|
| `php/src/Song.php` | Top-level song wrapper |
| `php/src/Group.php` | Group (song part) wrapper |
| `php/src/Slide.php` | Slide wrapper with text access |
| `php/src/TextElement.php` | Text element with RTF extraction |
| `php/src/Arrangement.php` | Arrangement wrapper |
| `php/src/RtfExtractor.php` | RTF to plain text converter |
| `php/src/ProFileReader.php` | Reads `.pro` files |
| `php/src/ProFileWriter.php` | Writes `.pro` files |
| `php/src/ProFileGenerator.php` | Generates `.pro` files |
| `php/bin/parse-song.php` | CLI tool |

---

## See Also

- [Format Specification](../formats/pp_song_spec.md) — Binary format details
- [Playlist API](playlist.md) — `.proplaylist` file handling
