- Move src/, tests/, bin/, generated/, proto/, composer.json, composer.lock, phpunit.xml from php/ to repo root - Move ref/ to doc/reference_samples/ for better organization - Remove vendor/ from git tracking (now properly gitignored) - Update all test file paths (dirname adjustments and ref/ -> doc/reference_samples/) - Update all documentation paths (AGENTS.md, doc/*.md) - Remove php.bak/ directory - All 252 tests pass
7.1 KiB
7.1 KiB
Song Parser API
PHP module for reading, modifying, and generating ProPresenter
.prosong files.
Quick Reference
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
use ProPresenter\Parser\ProFileReader;
$song = ProFileReader::read('path/to/song.pro');
Metadata Access
// 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.).
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.
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.
if ($slide->hasTranslation()) {
$translation = $slide->getTranslation();
$translation->getPlainText(); // Translated text
}
Macros
if ($slide->hasMacro()) {
$slide->getMacroName(); // "Lied 1.Folie"
$slide->getMacroUuid(); // "20C1DFDE-..."
$slide->getMacroCollectionName(); // "--MAIN--"
$slide->getMacroCollectionUuid(); // "8D02FC57-..."
}
Media
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.
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
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
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
// 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
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
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 |
|---|---|
src/Song.php |
Top-level song wrapper |
src/Group.php |
Group (song part) wrapper |
src/Slide.php |
Slide wrapper with text access |
src/TextElement.php |
Text element with RTF extraction |
src/Arrangement.php |
Arrangement wrapper |
src/RtfExtractor.php |
RTF to plain text converter |
src/ProFileReader.php |
Reads .pro files |
src/ProFileWriter.php |
Writes .pro files |
src/ProFileGenerator.php |
Generates .pro files |
bin/parse-song.php |
CLI tool |
See Also
- Format Specification — Binary format details
- Playlist API —
.proplaylistfile handling - Bundle API —
.probundlefile handling (Song objects inside bundles)