303 lines
7.1 KiB
Markdown
303 lines
7.1 KiB
Markdown
# 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
|
|
- [Bundle API](bundle.md) — `.probundle` file handling (Song objects inside bundles)
|