- Add ProPlaylistIntegrationTest with 8 round-trip tests - All 4 .proplaylist test files validated in ProPlaylistReaderTest - Update AGENTS.md with playlist module documentation - Document reading, writing, generating, CLI usage - Add notepad learnings from Wave 4 tasks
303 lines
9.5 KiB
Markdown
303 lines
9.5 KiB
Markdown
Analyze a file format of a song.
|
|
|
|
## Spec
|
|
|
|
File: ./Test.pro (file ext are always .pro)
|
|
|
|
- every song contains parts (name group here) (here: Verse 1, Verse 2, Chorus, ...) but could be any name
|
|
- every group contains 1-x slides
|
|
- every song contains different arrangements (here normal and test2) that defines the existence and the order of the groups
|
|
- every slide CAN have another textbox which contains a translated version of the first textbox
|
|
|
|
## Status
|
|
|
|
1. [x] Analyse the file structure and find all of the described specs.
|
|
2. [x] Test and verify if the definition is correct - there is a `all-songs` directory with lot of examples.
|
|
3. [x] Describe the structure for future AI prompts to use these files in `spec/pp_song_spec.md` and describe the usage in the `AGENTS.md` (replace obsolete commands)
|
|
4. [x] Write a PHP module (is later used in laravel) in `./php` which can parse a song and let get/set every aspect of structure. Use Objects here (Song, Group, Slide, Arrangement, etc)
|
|
5. [x] Create a simple PHP cli tool, which receive a param with a song file and show the structure of the song.
|
|
|
|
## PHP Module Usage
|
|
|
|
The ProPresenter song parser is available as a PHP module in `./php`. Use it to read, parse, and modify .pro song files.
|
|
|
|
### Reading a Song
|
|
|
|
```php
|
|
use ProPresenter\Parser\ProFileReader;
|
|
use ProPresenter\Parser\ProFileWriter;
|
|
|
|
$song = ProFileReader::read('path/to/song.pro');
|
|
```
|
|
|
|
### Accessing Song Structure
|
|
|
|
```php
|
|
// Basic song info
|
|
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.
|
|
$slides = $song->getSlidesForGroup($group);
|
|
foreach ($slides as $slide) {
|
|
echo $slide->getPlainText();
|
|
|
|
// Optional cue label/title
|
|
echo $slide->getLabel();
|
|
|
|
if ($slide->hasTranslation()) {
|
|
echo $slide->getTranslation()->getPlainText();
|
|
}
|
|
|
|
// Optional macro action on the cue
|
|
if ($slide->hasMacro()) {
|
|
echo $slide->getMacroName();
|
|
echo $slide->getMacroUuid();
|
|
echo $slide->getMacroCollectionName();
|
|
echo $slide->getMacroCollectionUuid();
|
|
}
|
|
|
|
// Optional media action on the cue (image/video)
|
|
if ($slide->hasMedia()) {
|
|
echo $slide->getMediaUrl();
|
|
echo $slide->getMediaUuid();
|
|
echo $slide->getMediaFormat();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Arrangements (different song orderings)
|
|
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'],
|
|
['text' => 'Line 3', 'macro' => ['name' => 'Lied 1.Folie', 'uuid' => '20C1DFDE-0FB6-49E5-B90C-E6608D427212']],
|
|
]],
|
|
['name' => 'Media', 'color' => [0.2, 0.2, 0.2, 1.0], 'slides' => [
|
|
['media' => 'file:///Users/me/Pictures/slide.jpg', 'format' => 'JPG', 'label' => 'slide.jpg'],
|
|
['media' => 'file:///Users/me/Pictures/slide2.jpg', 'format' => 'JPG', 'label' => 'slide2.jpg', 'macro' => ['name' => '1:1 - Beamer & Stream', 'uuid' => 'A5911D80-622E-4AD6-A242-9278D0640048']],
|
|
]],
|
|
['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);
|
|
```
|
|
|
|
### Edit Label/Macro/Media Data
|
|
|
|
```php
|
|
$slide = $song->getSlides()[0];
|
|
|
|
// Label (Cue.name)
|
|
$slide->setLabel('Seniorennachmittag März.jpg');
|
|
|
|
// Macro action
|
|
$slide->setMacro(
|
|
'Lied 1.Folie',
|
|
'20C1DFDE-0FB6-49E5-B90C-E6608D427212',
|
|
'--MAIN--',
|
|
'8D02FC57-83F8-4042-9B90-81C229728426',
|
|
);
|
|
|
|
// Remove macro action
|
|
$slide->removeMacro();
|
|
|
|
// Read media action
|
|
if ($slide->hasMedia()) {
|
|
$url = $slide->getMediaUrl();
|
|
$format = $slide->getMediaFormat();
|
|
}
|
|
```
|
|
|
|
## CLI Tool
|
|
|
|
Parse and display song structure from the command line:
|
|
|
|
```bash
|
|
php php/bin/parse-song.php path/to/song.pro
|
|
```
|
|
|
|
## Format Specification
|
|
|
|
For detailed information about the .pro file format, see `spec/pp_song_spec.md`.
|
|
|
|
## Key Files
|
|
|
|
- `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
|
|
- `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 from scratch
|
|
- `php/bin/parse-song.php` — CLI tool (shows metadata, groups, slides, arrangements)
|
|
- `spec/pp_song_spec.md` — Format specification
|
|
|
|
---
|
|
|
|
# ProPresenter Playlist Parser
|
|
|
|
Analyze and manage .proplaylist files.
|
|
|
|
## Spec
|
|
|
|
File: ./Test.proplaylist (file ext are always .proplaylist)
|
|
|
|
- every playlist is a ZIP archive containing metadata and embedded songs
|
|
- every playlist contains entries (songs or groups) with type-specific data
|
|
- entries can reference embedded songs or external song files
|
|
- songs are lazily parsed on demand to optimize performance
|
|
- playlists support custom metadata (name, notes, etc.)
|
|
|
|
## PHP Module Usage
|
|
|
|
The ProPresenter playlist parser is available as a PHP module in `./php`. Use it to read, parse, and modify .proplaylist files.
|
|
|
|
### Reading a Playlist
|
|
|
|
```php
|
|
use ProPresenter\Parser\ProPlaylistReader;
|
|
use ProPresenter\Parser\ProPlaylistWriter;
|
|
|
|
$archive = ProPlaylistReader::read('path/to/playlist.proplaylist');
|
|
```
|
|
|
|
### Accessing Playlist Structure
|
|
|
|
```php
|
|
// Basic playlist info
|
|
echo $archive->getName(); // Playlist name
|
|
echo $archive->getUuid(); // Playlist UUID
|
|
|
|
// Metadata
|
|
echo $archive->getNotes(); // Playlist notes
|
|
|
|
// Entries (songs or groups)
|
|
foreach ($archive->getEntries() as $entry) {
|
|
echo $entry->getType(); // 'song' or 'group'
|
|
echo $entry->getName(); // Entry name
|
|
echo $entry->getUuid(); // Entry UUID
|
|
|
|
// For song entries
|
|
if ($entry->getType() === 'song') {
|
|
echo $entry->getPath(); // File path or embedded reference
|
|
|
|
// Lazy-load embedded song
|
|
if ($entry->isEmbedded()) {
|
|
$song = $archive->getEmbeddedSong($entry);
|
|
echo $song->getName();
|
|
foreach ($song->getGroups() as $group) {
|
|
echo $group->getName();
|
|
}
|
|
}
|
|
}
|
|
|
|
// For group entries
|
|
if ($entry->getType() === 'group') {
|
|
$children = $entry->getChildren();
|
|
foreach ($children as $child) {
|
|
echo $child->getName();
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Modifying and Writing
|
|
|
|
```php
|
|
$archive->setName("New Playlist Name");
|
|
$archive->setNotes("Updated notes");
|
|
ProPlaylistWriter::write($archive, 'output.proplaylist');
|
|
```
|
|
|
|
### Generating a New Playlist
|
|
|
|
```php
|
|
use ProPresenter\Parser\ProPlaylistGenerator;
|
|
|
|
$archive = ProPlaylistGenerator::generate(
|
|
'Playlist Name',
|
|
[
|
|
['type' => 'song', 'name' => 'Song 1', 'path' => 'file:///path/to/song1.pro'],
|
|
['type' => 'group', 'name' => 'Group 1', 'children' => [
|
|
['type' => 'song', 'name' => 'Song 2', 'path' => 'file:///path/to/song2.pro'],
|
|
['type' => 'song', 'name' => 'Song 3', 'path' => 'file:///path/to/song3.pro'],
|
|
]],
|
|
],
|
|
['notes' => 'Sunday Service']
|
|
);
|
|
|
|
// Or generate and write in one call
|
|
ProPlaylistGenerator::generateAndWrite('output.proplaylist', 'Playlist Name', $entries, $metadata);
|
|
```
|
|
|
|
## CLI Tool
|
|
|
|
Parse and display playlist structure from the command line:
|
|
|
|
```bash
|
|
php php/bin/parse-playlist.php path/to/playlist.proplaylist
|
|
```
|
|
|
|
## Format Specification
|
|
|
|
For detailed information about the .proplaylist file format, see `spec/pp_playlist_spec.md`.
|
|
|
|
## Key Files
|
|
|
|
- `php/src/PlaylistArchive.php` — Top-level playlist wrapper (metadata, entries, embedded songs)
|
|
- `php/src/PlaylistEntry.php` — Playlist entry wrapper (song or group)
|
|
- `php/src/ProPlaylistReader.php` — Reads .proplaylist files
|
|
- `php/src/ProPlaylistWriter.php` — Writes .proplaylist files
|
|
- `php/src/ProPlaylistGenerator.php` — Generates .proplaylist files from scratch
|
|
- `php/bin/parse-playlist.php` — CLI tool (shows metadata, entries, embedded songs)
|
|
- `spec/pp_playlist_spec.md` — Format specification
|