[AI] update spec and docs with metadata and generator usage

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Thorsten Bus 2026-03-01 16:34:17 +01:00
parent fd639e6938
commit f258f8f2ce
2 changed files with 196 additions and 34 deletions

View file

@ -37,6 +37,19 @@ $song = ProFileReader::read('path/to/song.pro');
echo $song->getName(); // Song name
echo $song->getUuid(); // Song UUID
// CCLI metadata
echo $song->getCcliAuthor(); // "Joel Houston, Matt Crocker"
echo $song->getCcliSongTitle(); // "Oceans (Where Feet May Fail)"
echo $song->getCcliPublisher(); // "2012 Hillsong Music Publishing"
echo $song->getCcliCopyrightYear(); // 2012
echo $song->getCcliSongNumber(); // 6428767
echo $song->getCcliDisplay(); // true
// Other metadata
echo $song->getCategory(); // ""
echo $song->getNotes(); // ""
echo $song->getSelectedArrangementUuid(); // "uuid-string"
// Groups (song parts like Verse 1, Chorus, etc.)
foreach ($song->getGroups() as $group) {
echo $group->getName(); // "Verse 1", "Chorus", etc.
@ -54,15 +67,44 @@ foreach ($song->getArrangements() as $arr) {
$groups = $song->getGroupsForArrangement($arr);
// Groups in arrangement order
}
```
### Modifying and Writing
```php
$song->setName("New Name");
$song->setCcliAuthor("Author Name");
$song->setCcliSongNumber(12345);
$song->setCategory("Worship");
$song->setNotes("Use acoustic intro");
ProFileWriter::write($song, 'output.pro');
```
### Generating a New Song
```php
use ProPresenter\Parser\ProFileGenerator;
$song = ProFileGenerator::generate(
'Song Name',
[
['name' => 'Verse 1', 'color' => [0.13, 0.59, 0.95, 1.0], 'slides' => [
['text' => 'Line 1'],
['text' => 'Line 2', 'translation' => 'Zeile 2'],
]],
['name' => 'Chorus', 'color' => [0.95, 0.27, 0.27, 1.0], 'slides' => [
['text' => 'Chorus text'],
]],
],
[
['name' => 'normal', 'groupNames' => ['Verse 1', 'Chorus', 'Verse 1']],
],
['author' => 'Author', 'song_title' => 'Title', 'copyright_year' => 2024],
);
// Or generate and write in one call
ProFileGenerator::generateAndWrite('output.pro', 'Song Name', $groups, $arrangements, $ccli);
```
## CLI Tool
Parse and display song structure from the command line:
@ -77,7 +119,7 @@ For detailed information about the .pro file format, see `spec/pp_song_spec.md`.
## Key Files
- `php/src/Song.php` — Top-level song wrapper
- `php/src/Song.php` — Top-level song wrapper (metadata, CCLI, groups, slides, arrangements)
- `php/src/Group.php` — Group (song part) wrapper
- `php/src/Slide.php` — Slide wrapper with text access
- `php/src/TextElement.php` — Text element with label + plain text
@ -85,5 +127,6 @@ For detailed information about the .pro file format, see `spec/pp_song_spec.md`.
- `php/src/RtfExtractor.php` — RTF to plain text converter
- `php/src/ProFileReader.php` — Reads .pro files
- `php/src/ProFileWriter.php` — Writes .pro files
- `php/bin/parse-song.php` — CLI tool
- `php/src/ProFileGenerator.php` — Generates .pro files from scratch
- `php/bin/parse-song.php` — CLI tool (shows metadata, groups, slides, arrangements)
- `spec/pp_song_spec.md` — Format specification

View file

@ -1,6 +1,6 @@
# ProPresenter 7 `.pro` File Format Specification
**Version:** 1.0
**Version:** 1.1
**Target Audience:** AI agents, automated parsers, developers
**Proto Source:** greyshirtguy/ProPresenter7-Proto v7.16.2 (MIT License)
@ -94,12 +94,38 @@ Presentation
| Field Path | Protobuf Type | Field Number | Description |
|------------|---------------|--------------|-------------|
| `name` | `string` | 1 | Song title (e.g., "Amazing Grace") |
| `uuid` | `rv.data.UUID` | 5 | Unique identifier for the presentation |
| `cues[]` | `rv.data.Cue` | 13 | Array of slides |
| `cue_groups[]` | `rv.data.Presentation.CueGroup` | 12 | Array of groups (song parts) |
| `application_info` | `rv.data.ApplicationInfo` | 1 | Platform and application version info |
| `uuid` | `rv.data.UUID` | 2 | Unique identifier for the presentation |
| `name` | `string` | 3 | Song title (e.g., "Amazing Grace") |
| `last_date_used` | `rv.data.Timestamp` | 4 | Last date the song was used |
| `last_modified_date` | `rv.data.Timestamp` | 5 | Last modification date |
| `category` | `string` | 6 | Optional category label |
| `notes` | `string` | 7 | Optional notes |
| `background` | `rv.data.Background` | 8 | Background color/image |
| `selected_arrangement` | `rv.data.UUID` | 10 | UUID of the currently selected arrangement |
| `arrangements[]` | `rv.data.Presentation.Arrangement` | 11 | Array of arrangements |
| `cue_groups[]` | `rv.data.Presentation.CueGroup` | 12 | Array of groups (song parts) |
| `cues[]` | `rv.data.Cue` | 13 | Array of slides |
| `ccli` | `rv.data.Presentation.CCLI` | 14 | CCLI licensing metadata |
| `timeline` | `rv.data.Presentation.Timeline` | 17 | Timeline with duration |
| `music_key` | `string` | 22 | Music key (rarely used) |
| `music` | `rv.data.Presentation.Music` | 23 | Music key scale data |
### Presentation.CCLI
CCLI (Christian Copyright Licensing International) metadata. Present in 157 out of 168 reference files.
| Field Path | Protobuf Type | Field Number | Description |
|------------|---------------|--------------|-------------|
| `author` | `string` | 1 | Song author(s) (e.g., "Joel Houston, Matt Crocker") |
| `artist_credits` | `string` | 2 | Artist credits (rarely used) |
| `song_title` | `string` | 3 | CCLI song title |
| `publisher` | `string` | 4 | Publisher (e.g., "2012 Hillsong Music Publishing") |
| `copyright_year` | `uint32` | 5 | Copyright year (e.g., 2012) |
| `song_number` | `uint32` | 6 | CCLI song number (e.g., 6428767) |
| `display` | `bool` | 7 | Whether to display CCLI info |
| `album` | `string` | 8 | Album name (rarely used) |
| `artwork` | `bytes` | 9 | Album artwork (rarely used) |
### Presentation.CueGroup
| Field Path | Protobuf Type | Field Number | Description |
@ -416,19 +442,28 @@ $song = ProFileReader::read('path/to/song.pro');
### Access Song Metadata
```php
// Song name
// Song name and UUID
$name = $song->getName(); // "Amazing Grace"
// Song UUID
$uuid = $song->getUuid(); // "A1B2C3D4-..."
// Groups
$groups = $song->getGroups(); // Group[]
// CCLI metadata
$author = $song->getCcliAuthor(); // "Joel Houston, Matt Crocker"
$title = $song->getCcliSongTitle(); // "Oceans (Where Feet May Fail)"
$publisher = $song->getCcliPublisher(); // "2012 Hillsong Music Publishing"
$year = $song->getCcliCopyrightYear(); // 2012
$number = $song->getCcliSongNumber(); // 6428767
$display = $song->getCcliDisplay(); // true
$credits = $song->getCcliArtistCredits(); // ""
$album = $song->getCcliAlbum(); // ""
// Slides
$slides = $song->getSlides(); // Slide[]
// Other metadata
$category = $song->getCategory(); // ""
$notes = $song->getNotes(); // ""
$selectedArr = $song->getSelectedArrangementUuid(); // "uuid-string"
// Arrangements
// Groups, Slides, Arrangements
$groups = $song->getGroups(); // Group[]
$slides = $song->getSlides(); // Slide[]
$arrangements = $song->getArrangements(); // Arrangement[]
```
@ -491,17 +526,23 @@ foreach ($slides as $slide) {
```php
use ProPresenter\Parser\ProFileWriter;
// Modify song
// Modify song metadata
$song->setName("New Song Title");
$song->setCategory("Worship");
$song->setNotes("Use acoustic intro");
// Modify CCLI metadata
$song->setCcliAuthor("Author Name");
$song->setCcliSongTitle("Song Title");
$song->setCcliPublisher("Publisher");
$song->setCcliCopyrightYear(2024);
$song->setCcliSongNumber(12345);
$song->setCcliDisplay(true);
// Modify group
$group = $song->getGroupByName("Verse 1");
$group->setName("Strophe 1");
// Modify arrangement
$arrangement = $song->getArrangementByName("normal");
$arrangement->setName("default");
// Write to file
ProFileWriter::write($song, 'output.pro');
```
@ -559,26 +600,84 @@ $song->getPresentation()->getArrangements()[] = $arrangement->getProto();
ProFileWriter::write($song, 'output.pro');
```
### Generate a New Song
```php
use ProPresenter\Parser\ProFileGenerator;
$song = ProFileGenerator::generate(
'Amazing Grace',
[
[
'name' => 'Verse 1',
'color' => [0.13, 0.59, 0.95, 1.0],
'slides' => [
['text' => 'Amazing grace, how sweet the sound'],
['text' => 'That saved a wretch like me'],
],
],
[
'name' => 'Chorus',
'color' => [0.95, 0.27, 0.27, 1.0],
'slides' => [
['text' => 'I once was lost, but now am found'],
],
],
],
[
['name' => 'normal', 'groupNames' => ['Verse 1', 'Chorus', 'Verse 1']],
],
[
'author' => 'John Newton',
'song_title' => 'Amazing Grace',
'copyright_year' => 1779,
],
);
// Write to file
ProFileGenerator::generateAndWrite('output.pro', 'Amazing Grace', $groups, $arrangements, $ccli);
```
### Generate a Song with Translations
```php
$song = ProFileGenerator::generate(
'Oceans',
[
[
'name' => 'Verse 1',
'color' => [0.13, 0.59, 0.95, 1.0],
'slides' => [
[
'text' => 'You call me out upon the waters',
'translation' => 'Du rufst mich auf das Wasser',
],
],
],
],
[
['name' => 'normal', 'groupNames' => ['Verse 1']],
],
);
```
---
## Appendix: Test.pro Structure
### Groups (4)
1. **Verse 1** → 1 slide
1. **Verse 1**2 slides
2. **Verse 2** → 1 slide
3. **Chorus** → 2 slides
3. **Chorus**1 slide
4. **Ending** → 1 slide
### Slides (5)
- Slide 1: Verse 1 text (2 text elements: "Orginal", "Deutsch")
- Slide 2: Verse 2 text (2 text elements)
- Slide 3: Chorus text part 1 (2 text elements)
- Slide 4: Chorus text part 2 (2 text elements)
- Slide 5: Ending text (2 text elements)
- Slides 1-2: Verse 1 text (2 text elements each: "Orginal", "Deutsch")
- Slide 3: Verse 2 text (2 text elements)
- Slide 4: Chorus text (2 text elements)
- Slide 5: Ending text (2 text elements, with translations)
### Arrangements (2)
1. **normal:** Verse 1 → Chorus → Verse 2 → Chorus → Ending
2. **test2:** Verse 1 → Verse 2 → Chorus
1. **normal:** Chorus → Verse 1 → Chorus → Verse 2 → Chorus
2. **test2:** Verse 1 → Chorus → Verse 2 → Chorus
---
@ -588,6 +687,7 @@ ProFileWriter::write($song, 'output.pro');
- **Parseable Files:** 168
- **Empty Files:** 1 (invalid)
- **Files Without Arrangements:** 17 (valid)
- **Files With CCLI Data:** 157 out of 168
- **Binary Fidelity:** 0 files pass round-trip decode→encode (proto definitions incomplete)
---
@ -596,11 +696,30 @@ ProFileWriter::write($song, 'output.pro');
| Message | Field | Number |
|---------|-------|--------|
| Presentation | name | 1 |
| Presentation | uuid | 5 |
| Presentation | cues | 13 |
| Presentation | cue_groups | 12 |
| Presentation | application_info | 1 |
| Presentation | uuid | 2 |
| Presentation | name | 3 |
| Presentation | last_date_used | 4 |
| Presentation | last_modified_date | 5 |
| Presentation | category | 6 |
| Presentation | notes | 7 |
| Presentation | selected_arrangement | 10 |
| Presentation | arrangements | 11 |
| Presentation | cue_groups | 12 |
| Presentation | cues | 13 |
| Presentation | ccli | 14 |
| Presentation | timeline | 17 |
| Presentation | music_key | 22 |
| Presentation | music | 23 |
| Presentation.CCLI | author | 1 |
| Presentation.CCLI | artist_credits | 2 |
| Presentation.CCLI | song_title | 3 |
| Presentation.CCLI | publisher | 4 |
| Presentation.CCLI | copyright_year | 5 |
| Presentation.CCLI | song_number | 6 |
| Presentation.CCLI | display | 7 |
| Presentation.CCLI | album | 8 |
| Presentation.CCLI | artwork | 9 |
| CueGroup | group | 1 |
| CueGroup | cue_identifiers | 2 |
| Group | uuid | 1 |