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.
173 lines
5.1 KiB
Markdown
173 lines
5.1 KiB
Markdown
# Groups Library API
|
|
|
|
> PHP module for the global ProPresenter `Groups` file (raw protobuf, no
|
|
> extension). Exposes every named group definition (UUID, name, color,
|
|
> hot key) used to organise slides across songs and presentations.
|
|
|
|
## Quick Reference
|
|
|
|
```php
|
|
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`](../../proto/groups.proto):
|
|
|
|
| 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
|
|
|
|
```php
|
|
use ProPresenter\Parser\GroupsFileReader;
|
|
|
|
$library = GroupsFileReader::read('/Users/me/.../Groups');
|
|
```
|
|
|
|
Throws `InvalidArgumentException` for missing files and `RuntimeException`
|
|
for empty / unreadable files.
|
|
|
|
---
|
|
|
|
## Writing
|
|
|
|
```php
|
|
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.
|
|
|
|
```php
|
|
$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
|
|
|
|
```php
|
|
$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
|
|
|
|
```bash
|
|
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.
|