Add full IO support for every global ProPresenter library file plus theme folders, and extend the existing Labels/Macros readers with exporters and editable accessors so every supported document is now a round-trippable, mutable object. New library readers/writers (each: FileReader, FileWriter, Library wrapper, element wrapper where applicable, CLI tool, tests, doc/api/*.md): - Groups (ProGroupsDocument) + GroupDefinition - ClearGroups (ClearGroupsDocument) + ClearGroupDefinition - CCLI (CCLIDocument) - Messages (MessageDocument) + Message - Timers (TimersDocument + Clock) + Timer - Stage (Stage.Document) + StageLayout - Workspace (ProPresenterWorkspace) + Screen - Props (PropDocument) + Prop - TestPatterns (TestPatternDocument) - Calendar (new CalendarDocument) + CalendarEvent - KeyMappings (new KeyMappingsDocument) + KeyMapping - CommunicationDevices (JSON file) + CommunicationDevice - Theme bundles (Template.Document folder + Assets/) + ThemeBundle/Slide/Asset Extensions to existing modules: - LabelsFileWriter; Label and LabelLibrary gain setters, addLabel, removeLabel, setColor / setColorHex helpers - MacrosFileWriter; Macro/MacroCollection/MacroLibrary gain UUID, name, color, image_type, image_data, trigger_on_startup setters plus add/remove for macros and collections Two new minimal proto schemas were defined for documents that lacked upstream definitions: - proto/calendar.proto - CalendarDocument with Event entries, raw bytes for the action/macro sub-messages so the schema can evolve - proto/keyMappings.proto - KeyMappingsDocument with ApplicationInfo and a forward-looking Mapping message (sample only carries the info) The Theme file turned out to be a regular Rv\Data\Template\Document, so no new proto was required for theme content; ThemeBundle layers folder + Assets/ handling on top in the same spirit as PresentationBundle. GroupDefinition is intentionally distinct from the existing Group class (which wraps song-level CueGroup) to avoid breaking song APIs. Verified with the full PHPUnit suite: 370 tests, 9200 assertions, all green; LSP diagnostics clean across src/. The unmodified reference samples for Labels, Groups, ClearGroups, TestPatterns, Calendar and KeyMappings round-trip byte-for-byte; the others round-trip with the same byte length (PHP protobuf is not canonically deterministic but re-write-after-write stabilises). doc/INDEX.md, doc/keywords.md and AGENTS.md updated so every new module is discoverable from the top level.
5.1 KiB
Groups Library API
PHP module for the global ProPresenter
Groupsfile (raw protobuf, no extension). Exposes every named group definition (UUID, name, color, hot key) used to organise slides across songs and presentations.
Quick Reference
use ProPresenter\Parser\GroupsFileReader;
use ProPresenter\Parser\GroupsFileWriter;
$library = GroupsFileReader::read('/path/to/Groups');
foreach ($library->getGroups() as $group) {
$group->getName(); // "Verse 1"
$group->getUuid(); // "1D85C82C-EC82-44D8-8ED0-7742D46242C0"
$group->getColorHex(); // "#0077CC" | null
}
$library->addGroup('Bridge', '...uuid...');
GroupsFileWriter::write($library, '/path/to/Groups');
File Layout
The Groups file is the protobuf-serialised
ProGroupsDocument:
| Field | Type | Description |
|---|---|---|
groups |
repeated Group |
Library group definitions (UUID, name, color, hotKey) |
Each Group carries:
| Field | Type | Description |
|---|---|---|
uuid |
UUID |
Stable identifier referenced by song-level cue groups |
name |
string | Display name (e.g. "Verse 1") |
color |
Color (optional) |
RGBA float channels |
hotKey |
HotKey (optional) |
Keyboard shortcut binding |
application_group_identifier |
UUID (optional) |
Parent application group |
application_group_name |
string (optional) | Parent application group name |
Groups are identified by UUID; names should be unique but the format does not enforce it.
Reading
use ProPresenter\Parser\GroupsFileReader;
$library = GroupsFileReader::read('/Users/me/.../Groups');
Throws InvalidArgumentException for missing files and RuntimeException
for empty / unreadable files.
Writing
use ProPresenter\Parser\GroupsFileWriter;
GroupsFileWriter::write($library, '/Users/me/.../Groups');
The writer serialises the underlying ProGroupsDocument back to bytes and
saves them. The unmodified reference sample round-trips byte-for-byte.
GroupLibrary
Top-level wrapper around Rv\Data\ProGroupsDocument. Indexes groups by
UUID (case-insensitive) and by name for fast lookup.
$library->getGroups(); // GroupDefinition[]
$library->count(); // int
$library->getGroupByUuid('1D85C82C-...'); // ?GroupDefinition (case-insensitive)
$library->getGroupByName('Verse 1'); // ?GroupDefinition
$library->addGroup('Bridge', '...uuid...'); // GroupDefinition
$library->removeGroup('...uuid...'); // bool
$library->getDocument(); // \Rv\Data\ProGroupsDocument
If the same UUID or name appears more than once the first occurrence wins
for lookups; every entry is preserved in getGroups() in document order.
GroupDefinition
$group->getUuid(); // "1D85C82C-EC82-44D8-8ED0-7742D46242C0"
$group->setUuid('...'); // self
$group->getName(); // "Verse 1"
$group->setName('Verse 2'); // self
$group->getColor(); // ['r'=>..,'g'=>..,'b'=>..,'a'=>..] | null
$group->getColorHex(); // "#0077CC" | null
$group->setColor(['r'=>1, 'g'=>0, 'b'=>0]); // self
$group->getHotKey(); // ?\Rv\Data\HotKey
$group->getApplicationGroupName(); // string
$group->getApplicationGroupUuid(); // string
$group->getProto(); // \Rv\Data\Group (raw protobuf)
The GroupDefinition class name is intentionally distinct from the
existing Group class which wraps song-level CueGroup objects (slide
references, not library definitions).
CLI Tool
php bin/parse-groups.php /path/to/Groups
Output:
Groups (29):
[1] Vers :: 4E9D56A2-7E96-4975-97CC-44982257EF8A :: #0077CC
[2] Verse 1 :: 1D85C82C-EC82-44D8-8ED0-7742D46242C0 :: #0077CC
...
Key Files
| File | Purpose |
|---|---|
src/GroupLibrary.php |
Document-level wrapper with name / UUID lookup |
src/GroupDefinition.php |
Single library group (distinct from Group / CueGroup) |
src/GroupsFileReader.php |
Reads the Groups file |
src/GroupsFileWriter.php |
Writes the Groups file |
bin/parse-groups.php |
CLI tool |
proto/groups.proto |
Protobuf schema |
generated/Rv/Data/ProGroupsDocument.php |
Generated message class |
generated/Rv/Data/Group.php |
Generated group message class |
Naming Disambiguation
The codebase has two Group-shaped classes for two different scopes:
| Class | Scope | Wraps |
|---|---|---|
Group |
Song-level slide collection | Rv\Data\Presentation\CueGroup |
GroupDefinition |
Library-level group definition | Rv\Data\Group |
Songs reference library groups by UUID. The two classes co-exist because ProPresenter's data model has the same name in both places.
Scope Notes
This module covers reading and writing the Groups document. Wiring up
hot keys to actions and editing application group hierarchies are out of
scope; reach for getHotKey() / getProto() to inspect them when needed.