From 9e3e719806d8db3941444b8424fdd56b3b534aa8 Mon Sep 17 00:00:00 2001 From: Thorsten Bus Date: Sun, 3 May 2026 21:40:09 +0200 Subject: [PATCH] feat(library): add readers + writers for all ProPresenter global libraries and theme bundles 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. --- AGENTS.md | 39 ++- bin/parse-calendar.php | 26 ++ bin/parse-ccli.php | 25 ++ bin/parse-clear-groups.php | 35 ++ bin/parse-communication-devices.php | 29 ++ bin/parse-groups.php | 35 ++ bin/parse-key-mappings.php | 35 ++ bin/parse-messages.php | 27 ++ bin/parse-props.php | 25 ++ bin/parse-stage.php | 25 ++ bin/parse-test-patterns.php | 34 ++ bin/parse-theme.php | 30 ++ bin/parse-timers.php | 29 ++ bin/parse-workspace.php | 25 ++ doc/INDEX.md | 108 +++++- doc/api/calendar.md | 115 +++++++ doc/api/ccli.md | 104 ++++++ doc/api/clear-groups.md | 135 ++++++++ doc/api/communication-devices.md | 96 ++++++ doc/api/groups.md | 172 ++++++++++ doc/api/key-mappings.md | 130 +++++++ doc/api/labels.md | 53 ++- doc/api/macros.md | 73 +++- doc/api/messages.md | 110 ++++++ doc/api/props.md | 80 +++++ doc/api/stage.md | 83 +++++ doc/api/test-patterns.md | 106 ++++++ doc/api/theme.md | 116 +++++++ doc/api/timers.md | 106 ++++++ doc/api/workspace.md | 83 +++++ doc/keywords.md | 103 +++++- doc/reference_samples/CCLI | Bin 0 -> 1292 bytes doc/reference_samples/Calendar | 49 +++ doc/reference_samples/ClearGroups | Bin 0 -> 151 bytes doc/reference_samples/CommunicationDevices | 1 + doc/reference_samples/Groups | Bin 0 -> 2110 bytes doc/reference_samples/KeyMappings | 2 + doc/reference_samples/Messages | Bin 0 -> 462 bytes doc/reference_samples/Props | Bin 0 -> 17779 bytes doc/reference_samples/Stage | Bin 0 -> 57284 bytes doc/reference_samples/TestPatterns | 3 + doc/reference_samples/Timers | Bin 0 -> 420 bytes doc/reference_samples/Workspace | Bin 0 -> 12739 bytes doc/reference_samples/pp-config/CCLI | Bin 0 -> 1292 bytes doc/reference_samples/pp-config/Calendar | 49 +++ doc/reference_samples/pp-config/ClearGroups | Bin 0 -> 151 bytes .../pp-config/CommunicationDevices | 1 + doc/reference_samples/pp-config/Groups | Bin 0 -> 2110 bytes doc/reference_samples/pp-config/KeyMappings | 2 + doc/reference_samples/pp-config/Labels | Bin 0 -> 527 bytes doc/reference_samples/pp-config/Macros | Bin 0 -> 9529 bytes doc/reference_samples/pp-config/Messages | Bin 0 -> 462 bytes doc/reference_samples/pp-config/Props | Bin 0 -> 17779 bytes doc/reference_samples/pp-config/Stage | Bin 0 -> 57284 bytes doc/reference_samples/pp-config/TestPatterns | 3 + doc/reference_samples/pp-config/Timers | Bin 0 -> 420 bytes doc/reference_samples/pp-config/Workspace | Bin 0 -> 12739 bytes .../pp-themes/sample/Assets/BACKGROUND.jpg | Bin 0 -> 142735 bytes .../sample/Assets/BAUCHBIND_STREAM.jpg | Bin 0 -> 27812 bytes .../pp-themes/sample/Assets/KEY_VISUAL.jpg | Bin 0 -> 159459 bytes doc/reference_samples/pp-themes/sample/Theme | Bin 0 -> 31038 bytes generated/GPBMetadata/Calendar.php | 27 ++ generated/GPBMetadata/KeyMappings.php | 28 ++ generated/Rv/Data/CalendarDocument.php | 107 ++++++ generated/Rv/Data/CalendarDocument/Event.php | 316 ++++++++++++++++++ generated/Rv/Data/KeyMappingsDocument.php | 115 +++++++ .../Rv/Data/KeyMappingsDocument/Mapping.php | 196 +++++++++++ proto/calendar.proto | 51 +++ proto/keyMappings.proto | 39 +++ src/CCLIFileReader.php | 38 +++ src/CCLIFileWriter.php | 26 ++ src/CCLILibrary.php | 103 ++++++ src/CalendarEvent.php | 140 ++++++++ src/CalendarFileReader.php | 37 ++ src/CalendarFileWriter.php | 24 ++ src/CalendarLibrary.php | 127 +++++++ src/ClearGroupDefinition.php | 199 +++++++++++ src/ClearGroupsFileReader.php | 38 +++ src/ClearGroupsFileWriter.php | 26 ++ src/ClearGroupsLibrary.php | 119 +++++++ src/CommunicationDevice.php | 75 +++++ src/CommunicationDevicesFileReader.php | 25 ++ src/CommunicationDevicesFileWriter.php | 25 ++ src/CommunicationDevicesLibrary.php | 94 ++++++ src/GroupDefinition.php | 119 +++++++ src/GroupLibrary.php | 117 +++++++ src/GroupsFileReader.php | 38 +++ src/GroupsFileWriter.php | 26 ++ src/KeyMapping.php | 82 +++++ src/KeyMappingsFileReader.php | 38 +++ src/KeyMappingsFileWriter.php | 26 ++ src/KeyMappingsLibrary.php | 139 ++++++++ src/Label.php | 75 +++-- src/LabelLibrary.php | 105 ++++-- src/LabelsFileWriter.php | 26 ++ src/Macro.php | 92 +++-- src/MacroCollection.php | 66 +++- src/MacroLibrary.php | 191 ++++++++--- src/MacrosFileWriter.php | 26 ++ src/Message.php | 103 ++++++ src/MessageLibrary.php | 123 +++++++ src/MessagesFileReader.php | 38 +++ src/MessagesFileWriter.php | 24 ++ src/Prop.php | 77 +++++ src/PropLibrary.php | 115 +++++++ src/PropsFileReader.php | 35 ++ src/PropsFileWriter.php | 22 ++ src/Screen.php | 71 ++++ src/StageFileReader.php | 38 +++ src/StageFileWriter.php | 24 ++ src/StageLayout.php | 53 +++ src/StageLibrary.php | 115 +++++++ src/TestPatternsFileReader.php | 38 +++ src/TestPatternsFileWriter.php | 26 ++ src/TestPatternsLibrary.php | 161 +++++++++ src/ThemeAsset.php | 45 +++ src/ThemeBundle.php | 135 ++++++++ src/ThemeFileReader.php | 68 ++++ src/ThemeFileWriter.php | 51 +++ src/ThemeSlide.php | 38 +++ src/Timer.php | 78 +++++ src/TimersFileReader.php | 37 ++ src/TimersFileWriter.php | 24 ++ src/TimersLibrary.php | 136 ++++++++ src/WorkspaceFileReader.php | 35 ++ src/WorkspaceFileWriter.php | 22 ++ src/WorkspaceLibrary.php | 137 ++++++++ tests/CCLIFileReaderTest.php | 93 ++++++ tests/CalendarFileReaderTest.php | 91 +++++ tests/ClearGroupsFileReaderTest.php | 119 +++++++ tests/CommunicationDevicesFileReaderTest.php | 89 +++++ tests/GroupsFileReaderTest.php | 124 +++++++ tests/KeyMappingsFileReaderTest.php | 103 ++++++ tests/LabelsFileWriterTest.php | 71 ++++ tests/MacrosFileWriterTest.php | 83 +++++ tests/MessagesFileReaderTest.php | 88 +++++ tests/PropsFileReaderTest.php | 83 +++++ tests/StageFileReaderTest.php | 86 +++++ tests/TestPatternsFileReaderTest.php | 99 ++++++ tests/ThemeFileReaderTest.php | 148 ++++++++ tests/TimersFileReaderTest.php | 94 ++++++ tests/WorkspaceFileReaderTest.php | 83 +++++ 142 files changed, 8557 insertions(+), 209 deletions(-) create mode 100755 bin/parse-calendar.php create mode 100644 bin/parse-ccli.php create mode 100644 bin/parse-clear-groups.php create mode 100755 bin/parse-communication-devices.php create mode 100755 bin/parse-groups.php create mode 100644 bin/parse-key-mappings.php create mode 100755 bin/parse-messages.php create mode 100644 bin/parse-props.php create mode 100644 bin/parse-stage.php create mode 100644 bin/parse-test-patterns.php create mode 100644 bin/parse-theme.php create mode 100755 bin/parse-timers.php create mode 100644 bin/parse-workspace.php create mode 100644 doc/api/calendar.md create mode 100644 doc/api/ccli.md create mode 100644 doc/api/clear-groups.md create mode 100644 doc/api/communication-devices.md create mode 100644 doc/api/groups.md create mode 100644 doc/api/key-mappings.md create mode 100644 doc/api/messages.md create mode 100644 doc/api/props.md create mode 100644 doc/api/stage.md create mode 100644 doc/api/test-patterns.md create mode 100644 doc/api/theme.md create mode 100644 doc/api/timers.md create mode 100644 doc/api/workspace.md create mode 100644 doc/reference_samples/CCLI create mode 100644 doc/reference_samples/Calendar create mode 100644 doc/reference_samples/ClearGroups create mode 100644 doc/reference_samples/CommunicationDevices create mode 100644 doc/reference_samples/Groups create mode 100644 doc/reference_samples/KeyMappings create mode 100644 doc/reference_samples/Messages create mode 100644 doc/reference_samples/Props create mode 100644 doc/reference_samples/Stage create mode 100644 doc/reference_samples/TestPatterns create mode 100644 doc/reference_samples/Timers create mode 100644 doc/reference_samples/Workspace create mode 100644 doc/reference_samples/pp-config/CCLI create mode 100644 doc/reference_samples/pp-config/Calendar create mode 100644 doc/reference_samples/pp-config/ClearGroups create mode 100644 doc/reference_samples/pp-config/CommunicationDevices create mode 100644 doc/reference_samples/pp-config/Groups create mode 100644 doc/reference_samples/pp-config/KeyMappings create mode 100644 doc/reference_samples/pp-config/Labels create mode 100644 doc/reference_samples/pp-config/Macros create mode 100644 doc/reference_samples/pp-config/Messages create mode 100644 doc/reference_samples/pp-config/Props create mode 100644 doc/reference_samples/pp-config/Stage create mode 100644 doc/reference_samples/pp-config/TestPatterns create mode 100644 doc/reference_samples/pp-config/Timers create mode 100644 doc/reference_samples/pp-config/Workspace create mode 100644 doc/reference_samples/pp-themes/sample/Assets/BACKGROUND.jpg create mode 100644 doc/reference_samples/pp-themes/sample/Assets/BAUCHBIND_STREAM.jpg create mode 100644 doc/reference_samples/pp-themes/sample/Assets/KEY_VISUAL.jpg create mode 100644 doc/reference_samples/pp-themes/sample/Theme create mode 100644 generated/GPBMetadata/Calendar.php create mode 100644 generated/GPBMetadata/KeyMappings.php create mode 100644 generated/Rv/Data/CalendarDocument.php create mode 100644 generated/Rv/Data/CalendarDocument/Event.php create mode 100644 generated/Rv/Data/KeyMappingsDocument.php create mode 100644 generated/Rv/Data/KeyMappingsDocument/Mapping.php create mode 100644 proto/calendar.proto create mode 100644 proto/keyMappings.proto create mode 100644 src/CCLIFileReader.php create mode 100644 src/CCLIFileWriter.php create mode 100644 src/CCLILibrary.php create mode 100644 src/CalendarEvent.php create mode 100644 src/CalendarFileReader.php create mode 100644 src/CalendarFileWriter.php create mode 100644 src/CalendarLibrary.php create mode 100644 src/ClearGroupDefinition.php create mode 100644 src/ClearGroupsFileReader.php create mode 100644 src/ClearGroupsFileWriter.php create mode 100644 src/ClearGroupsLibrary.php create mode 100644 src/CommunicationDevice.php create mode 100644 src/CommunicationDevicesFileReader.php create mode 100644 src/CommunicationDevicesFileWriter.php create mode 100644 src/CommunicationDevicesLibrary.php create mode 100644 src/GroupDefinition.php create mode 100644 src/GroupLibrary.php create mode 100644 src/GroupsFileReader.php create mode 100644 src/GroupsFileWriter.php create mode 100644 src/KeyMapping.php create mode 100644 src/KeyMappingsFileReader.php create mode 100644 src/KeyMappingsFileWriter.php create mode 100644 src/KeyMappingsLibrary.php create mode 100644 src/LabelsFileWriter.php create mode 100644 src/MacrosFileWriter.php create mode 100644 src/Message.php create mode 100644 src/MessageLibrary.php create mode 100644 src/MessagesFileReader.php create mode 100644 src/MessagesFileWriter.php create mode 100644 src/Prop.php create mode 100644 src/PropLibrary.php create mode 100644 src/PropsFileReader.php create mode 100644 src/PropsFileWriter.php create mode 100644 src/Screen.php create mode 100644 src/StageFileReader.php create mode 100644 src/StageFileWriter.php create mode 100644 src/StageLayout.php create mode 100644 src/StageLibrary.php create mode 100644 src/TestPatternsFileReader.php create mode 100644 src/TestPatternsFileWriter.php create mode 100644 src/TestPatternsLibrary.php create mode 100644 src/ThemeAsset.php create mode 100644 src/ThemeBundle.php create mode 100644 src/ThemeFileReader.php create mode 100644 src/ThemeFileWriter.php create mode 100644 src/ThemeSlide.php create mode 100644 src/Timer.php create mode 100644 src/TimersFileReader.php create mode 100644 src/TimersFileWriter.php create mode 100644 src/TimersLibrary.php create mode 100644 src/WorkspaceFileReader.php create mode 100644 src/WorkspaceFileWriter.php create mode 100644 src/WorkspaceLibrary.php create mode 100644 tests/CCLIFileReaderTest.php create mode 100644 tests/CalendarFileReaderTest.php create mode 100644 tests/ClearGroupsFileReaderTest.php create mode 100644 tests/CommunicationDevicesFileReaderTest.php create mode 100644 tests/GroupsFileReaderTest.php create mode 100644 tests/KeyMappingsFileReaderTest.php create mode 100644 tests/LabelsFileWriterTest.php create mode 100644 tests/MacrosFileWriterTest.php create mode 100644 tests/MessagesFileReaderTest.php create mode 100644 tests/PropsFileReaderTest.php create mode 100644 tests/StageFileReaderTest.php create mode 100644 tests/TestPatternsFileReaderTest.php create mode 100644 tests/ThemeFileReaderTest.php create mode 100644 tests/TimersFileReaderTest.php create mode 100644 tests/WorkspaceFileReaderTest.php diff --git a/AGENTS.md b/AGENTS.md index 251c55d..d1c90d5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,8 +19,21 @@ All project documentation lives in `doc/`. Load only what you need. | Parse/modify `.pro` song files | `doc/api/song.md` | | Parse/modify `.proplaylist` files | `doc/api/playlist.md` | | Parse/modify `.probundle` files | `doc/api/bundle.md` | -| Read the global `Macros` file | `doc/api/macros.md` | -| Read the global `Labels` file | `doc/api/labels.md` | +| Read/write the global `Macros` file | `doc/api/macros.md` | +| Read/write the global `Labels` file | `doc/api/labels.md` | +| Read/write the global `Groups` file | `doc/api/groups.md` | +| Read/write the global `ClearGroups` file | `doc/api/clear-groups.md` | +| Read/write the global `CCLI` file | `doc/api/ccli.md` | +| Read/write the global `Messages` file | `doc/api/messages.md` | +| Read/write the global `Timers` file | `doc/api/timers.md` | +| Read/write the global `Stage` file | `doc/api/stage.md` | +| Read/write the global `Workspace` file | `doc/api/workspace.md` | +| Read/write the global `Props` file | `doc/api/props.md` | +| Read/write the global `TestPatterns` file | `doc/api/test-patterns.md` | +| Read/write the global `Calendar` file | `doc/api/calendar.md` | +| Read/write the global `KeyMappings` file | `doc/api/key-mappings.md` | +| Read/write the `CommunicationDevices` JSON file | `doc/api/communication-devices.md` | +| Read/write a theme folder (Theme + Assets/) | `doc/api/theme.md` | | Understand `.pro` binary format | `doc/formats/pp_song_spec.md` | | Understand `.proplaylist` binary format | `doc/formats/pp_playlist_spec.md` | | Understand `.probundle` binary format | `doc/formats/pp_bundle_spec.md` | @@ -57,16 +70,34 @@ PHP tools for parsing, modifying, and generating ProPresenter 7 files: - **Songs** (`.pro`) — Protobuf-encoded presentation files with lyrics, groups, slides, arrangements, translations - **Playlists** (`.proplaylist`) — ZIP64 archives containing playlist metadata and embedded songs - **Bundles** (`.probundle`) — ZIP archives containing a single presentation with embedded media assets -- **Macros** (`Macros`, no extension) — Global protobuf-encoded macro library with collections -- **Labels** (`Labels`, no extension) — Global protobuf-encoded label library (slide labels with optional UI colors) +- **Themes** (folder with `Theme` + `Assets/`) — Template document plus media used as a slide theme +- **Global library files** (no extension) — `Macros`, `Labels`, `Groups`, `ClearGroups`, `CCLI`, `Messages`, `Timers`, `Stage`, `Workspace`, `Props`, `TestPatterns`, `Calendar`, `KeyMappings` (protobuf) and `CommunicationDevices` (JSON) ### CLI Tools ```bash +# Songs / playlists / bundles php bin/parse-song.php path/to/song.pro php bin/parse-playlist.php path/to/playlist.proplaylist + +# Global library files (one parser per type) php bin/parse-macros.php path/to/Macros php bin/parse-labels.php path/to/Labels +php bin/parse-groups.php path/to/Groups +php bin/parse-clear-groups.php path/to/ClearGroups +php bin/parse-ccli.php path/to/CCLI +php bin/parse-messages.php path/to/Messages +php bin/parse-timers.php path/to/Timers +php bin/parse-stage.php path/to/Stage +php bin/parse-workspace.php path/to/Workspace +php bin/parse-props.php path/to/Props +php bin/parse-test-patterns.php path/to/TestPatterns +php bin/parse-calendar.php path/to/Calendar +php bin/parse-key-mappings.php path/to/KeyMappings +php bin/parse-communication-devices.php path/to/CommunicationDevices + +# Theme folder +php bin/parse-theme.php path/to/ThemeFolder ``` ### Key Source Files diff --git a/bin/parse-calendar.php b/bin/parse-calendar.php new file mode 100755 index 0000000..cb6723a --- /dev/null +++ b/bin/parse-calendar.php @@ -0,0 +1,26 @@ +#!/usr/bin/env php +\n"; + exit(1); +} + +try { + $library = CalendarFileReader::read($argv[1]); +} catch (Exception $e) { + echo 'Error: ' . $e->getMessage() . "\n"; + exit(1); +} + +$events = $library->getEvents(); +echo 'Calendar events (' . count($events) . ') mode=' . $library->getMode() . ":\n"; +foreach ($events as $index => $event) { + echo sprintf(" [%d] %s :: %s :: start=%s :: end=%s :: action=%dB :: macro=%dB\n", $index + 1, $event->getName() === '' ? '(unnamed)' : $event->getName(), $event->getUuid(), (string) ($event->getStartTimeSeconds() ?? ''), (string) ($event->getEndTimeSeconds() ?? ''), strlen($event->getActionData()), strlen($event->getMacroData())); +} diff --git a/bin/parse-ccli.php b/bin/parse-ccli.php new file mode 100644 index 0000000..a7eb506 --- /dev/null +++ b/bin/parse-ccli.php @@ -0,0 +1,25 @@ +#!/usr/bin/env php +\n"; + exit(1); +} + +$filePath = $argv[1]; + +try { + $library = CCLIFileReader::read($filePath); +} catch (Exception $e) { + echo "Error: " . $e->getMessage() . "\n"; + exit(1); +} + +echo "CCLI (1):\n"; +echo sprintf(" [1] enabled=%s :: license=%s :: display_type=%d :: template=%s\n", $library->isCCLIDisplayEnabled() ? 'yes' : 'no', $library->getCCLILicense() === '' ? '(empty)' : $library->getCCLILicense(), $library->getDisplayType(), $library->getTemplate() === null ? 'no' : 'yes'); diff --git a/bin/parse-clear-groups.php b/bin/parse-clear-groups.php new file mode 100644 index 0000000..06d27c7 --- /dev/null +++ b/bin/parse-clear-groups.php @@ -0,0 +1,35 @@ +#!/usr/bin/env php +\n"; + exit(1); +} + +$filePath = $argv[1]; + +try { + $library = ClearGroupsFileReader::read($filePath); +} catch (Exception $e) { + echo "Error: " . $e->getMessage() . "\n"; + exit(1); +} + +$groups = $library->getGroups(); + +echo "ClearGroups (" . count($groups) . "):\n"; +foreach ($groups as $index => $group) { + $number = $index + 1; + $name = $group->getName(); + $displayName = $name === '' ? '(unnamed)' : $name; + $uuid = $group->getUuid(); + $colorPart = $group->getColorHex() ?? '(no tint)'; + + echo sprintf(" [%d] %s :: %s :: image_type=%d :: %s\n", $number, $displayName, $uuid, $group->getImageType(), $colorPart); +} diff --git a/bin/parse-communication-devices.php b/bin/parse-communication-devices.php new file mode 100755 index 0000000..2344e71 --- /dev/null +++ b/bin/parse-communication-devices.php @@ -0,0 +1,29 @@ +#!/usr/bin/env php +\n"; + exit(1); +} + +try { + $library = CommunicationDevicesFileReader::read($argv[1]); +} catch (Exception $e) { + echo 'Error: ' . $e->getMessage() . "\n"; + exit(1); +} + +$devices = $library->getDevices(); +echo 'Communication devices (' . count($devices) . "):\n"; +if ($devices === []) { + echo " (none configured)\n"; +} +foreach ($devices as $index => $device) { + echo sprintf(" [%d] %s :: %s :: %s :: %s\n", $index + 1, $device->getName() === '' ? '(unnamed)' : $device->getName(), $device->getId(), $device->getType(), $device->getAddress()); +} diff --git a/bin/parse-groups.php b/bin/parse-groups.php new file mode 100755 index 0000000..9915c58 --- /dev/null +++ b/bin/parse-groups.php @@ -0,0 +1,35 @@ +#!/usr/bin/env php +\n"; + exit(1); +} + +$filePath = $argv[1]; + +try { + $library = GroupsFileReader::read($filePath); +} catch (Exception $e) { + echo "Error: " . $e->getMessage() . "\n"; + exit(1); +} + +$groups = $library->getGroups(); + +echo "Groups (" . count($groups) . "):\n"; +foreach ($groups as $index => $group) { + $number = $index + 1; + $name = $group->getName(); + $displayName = $name === '' ? '(unnamed)' : $name; + $uuid = $group->getUuid(); + $colorPart = $group->getColorHex() ?? '(no color)'; + + echo sprintf(" [%d] %s :: %s :: %s\n", $number, $displayName, $uuid, $colorPart); +} diff --git a/bin/parse-key-mappings.php b/bin/parse-key-mappings.php new file mode 100644 index 0000000..14d8f4c --- /dev/null +++ b/bin/parse-key-mappings.php @@ -0,0 +1,35 @@ +#!/usr/bin/env php +\n"; + exit(1); +} + +$filePath = $argv[1]; + +try { + $library = KeyMappingsFileReader::read($filePath); +} catch (Exception $e) { + echo "Error: " . $e->getMessage() . "\n"; + exit(1); +} + +$mappings = $library->getMappings(); + +echo "KeyMappings (" . count($mappings) . "):\n"; +foreach ($mappings as $index => $mapping) { + $number = $index + 1; + $name = $mapping->getName(); + $displayName = $name === '' ? '(unnamed)' : $name; + $uuid = $mapping->getUuid(); + $hotKeyPart = $mapping->getHotKey() === null ? '(no hot key)' : 'hot_key=yes'; + + echo sprintf(" [%d] %s :: %s :: target_bytes=%d :: %s\n", $number, $displayName, $uuid, strlen($mapping->getTarget()), $hotKeyPart); +} diff --git a/bin/parse-messages.php b/bin/parse-messages.php new file mode 100755 index 0000000..cf108c4 --- /dev/null +++ b/bin/parse-messages.php @@ -0,0 +1,27 @@ +#!/usr/bin/env php +\n"; + exit(1); +} + +try { + $library = MessagesFileReader::read($argv[1]); +} catch (Exception $e) { + echo 'Error: ' . $e->getMessage() . "\n"; + exit(1); +} + +$messages = $library->getMessages(); +echo 'Messages (' . count($messages) . "):\n"; +foreach ($messages as $index => $message) { + $title = $message->getTitle() === '' ? '(untitled)' : $message->getTitle(); + echo sprintf(" [%d] %s :: %s :: clear=%d :: network=%s\n", $index + 1, $title, $message->getUuid(), $message->getClearType(), $message->isVisibleOnNetwork() ? 'yes' : 'no'); +} diff --git a/bin/parse-props.php b/bin/parse-props.php new file mode 100644 index 0000000..387c98f --- /dev/null +++ b/bin/parse-props.php @@ -0,0 +1,25 @@ +#!/usr/bin/env php +\n"; + exit(1); +} + +try { + $library = PropsFileReader::read($argv[1]); +} catch (Exception $e) { + echo 'Error: ' . $e->getMessage() . "\n"; + exit(1); +} + +echo 'Props (' . $library->count() . "):\n"; +foreach ($library->getProps() as $index => $prop) { + echo sprintf(" [%d] %s :: %s :: %s\n", $index + 1, $prop->getName() ?: '(unnamed)', $prop->getUuid(), $prop->isEnabled() ? 'enabled' : 'disabled'); +} diff --git a/bin/parse-stage.php b/bin/parse-stage.php new file mode 100644 index 0000000..0d14371 --- /dev/null +++ b/bin/parse-stage.php @@ -0,0 +1,25 @@ +#!/usr/bin/env php +\n"; + exit(1); +} + +try { + $library = StageFileReader::read($argv[1]); +} catch (Exception $e) { + echo 'Error: ' . $e->getMessage() . "\n"; + exit(1); +} + +echo 'Stage layouts (' . $library->count() . "):\n"; +foreach ($library->getLayouts() as $index => $layout) { + echo sprintf(" [%d] %s :: %s\n", $index + 1, $layout->getName() ?: '(unnamed)', $layout->getUuid()); +} diff --git a/bin/parse-test-patterns.php b/bin/parse-test-patterns.php new file mode 100644 index 0000000..4ba235e --- /dev/null +++ b/bin/parse-test-patterns.php @@ -0,0 +1,34 @@ +#!/usr/bin/env php +\n"; + exit(1); +} + +$filePath = $argv[1]; + +try { + $library = TestPatternsFileReader::read($filePath); +} catch (Exception $e) { + echo "Error: " . $e->getMessage() . "\n"; + exit(1); +} + +echo "TestPatterns (" . $library->count() . "):\n"; +echo sprintf(" State: selected=%s :: name=%s :: display_location=%d :: screen=%s\n", $library->getSelectedPatternUuid() === '' ? '(none)' : $library->getSelectedPatternUuid(), $library->getSelectedPatternNameLocalizationKey() === '' ? '(none)' : $library->getSelectedPatternNameLocalizationKey(), $library->getDisplayLocation(), $library->getSpecificScreenUuid() === '' ? '(none)' : $library->getSpecificScreenUuid()); + +foreach ($library->getPatterns() as $index => $pattern) { + $number = $index + 1; + $name = $pattern->getNameLocalizationKey(); + $displayName = $name === '' ? '(unnamed)' : $name; + $uuid = $pattern->getUuid()?->getString() ?? ''; + + echo sprintf(" [%d] %s :: %s\n", $number, $displayName, $uuid); +} diff --git a/bin/parse-theme.php b/bin/parse-theme.php new file mode 100644 index 0000000..c01f430 --- /dev/null +++ b/bin/parse-theme.php @@ -0,0 +1,30 @@ +#!/usr/bin/env php +\n"; + exit(1); +} + +try { + $bundle = ThemeFileReader::read($argv[1]); +} catch (Exception $e) { + echo 'Error: ' . $e->getMessage() . "\n"; + exit(1); +} + +echo "Theme folder: {$argv[1]}\n"; +echo 'Slides (' . $bundle->count() . "):\n"; +foreach ($bundle->getSlides() as $index => $slide) { + echo sprintf(" [%d] %s\n", $index + 1, $slide->getName() ?: '(unnamed)'); +} +echo 'Assets (' . $bundle->getAssetCount() . "):\n"; +foreach ($bundle->getAssets() as $index => $asset) { + echo sprintf(" [%d] Assets/%s :: %d bytes :: %s\n", $index + 1, $asset->getName(), $asset->getSize(), $asset->getMimeType()); +} diff --git a/bin/parse-timers.php b/bin/parse-timers.php new file mode 100755 index 0000000..a54a8d0 --- /dev/null +++ b/bin/parse-timers.php @@ -0,0 +1,29 @@ +#!/usr/bin/env php +\n"; + exit(1); +} + +try { + $library = TimersFileReader::read($argv[1]); +} catch (Exception $e) { + echo 'Error: ' . $e->getMessage() . "\n"; + exit(1); +} + +$timers = $library->getTimers(); +echo 'Timers (' . count($timers) . ') clock=' . $library->getClockFormat() . ":\n"; +foreach ($timers as $index => $timer) { + $type = $timer->isCountdown() ? 'countdown' : ($timer->isCountdownToTime() ? 'countdown_to_time' : ($timer->isElapsedTime() ? 'elapsed_time' : 'unknown')); + $duration = $timer->getDurationSeconds(); + $durationPart = $duration === null ? '' : sprintf(' :: %ds', $duration); + echo sprintf(" [%d] %s :: %s :: %s%s\n", $index + 1, $timer->getName() === '' ? '(unnamed)' : $timer->getName(), $timer->getUuid(), $type, $durationPart); +} diff --git a/bin/parse-workspace.php b/bin/parse-workspace.php new file mode 100644 index 0000000..68ff857 --- /dev/null +++ b/bin/parse-workspace.php @@ -0,0 +1,25 @@ +#!/usr/bin/env php +\n"; + exit(1); +} + +try { + $library = WorkspaceFileReader::read($argv[1]); +} catch (Exception $e) { + echo 'Error: ' . $e->getMessage() . "\n"; + exit(1); +} + +echo 'Screens (' . $library->count() . "):\n"; +foreach ($library->getScreens() as $index => $screen) { + echo sprintf(" [%d] %s :: %s :: type %d\n", $index + 1, $screen->getName() ?: '(unnamed)', $screen->getUuid(), $screen->getScreenType()); +} diff --git a/doc/INDEX.md b/doc/INDEX.md index 30a6323..153a221 100644 --- a/doc/INDEX.md +++ b/doc/INDEX.md @@ -9,8 +9,21 @@ | Parse/modify `.pro` song files | [api/song.md](api/song.md) | | Parse/modify `.proplaylist` files | [api/playlist.md](api/playlist.md) | | Parse/modify `.probundle` files | [api/bundle.md](api/bundle.md) | -| Read the global `Macros` file | [api/macros.md](api/macros.md) | -| Read the global `Labels` file | [api/labels.md](api/labels.md) | +| Read/write the global `Macros` file | [api/macros.md](api/macros.md) | +| Read/write the global `Labels` file | [api/labels.md](api/labels.md) | +| Read/write the global `Groups` file | [api/groups.md](api/groups.md) | +| Read/write the global `ClearGroups` file | [api/clear-groups.md](api/clear-groups.md) | +| Read/write the global `CCLI` file | [api/ccli.md](api/ccli.md) | +| Read/write the global `Messages` file | [api/messages.md](api/messages.md) | +| Read/write the global `Timers` file | [api/timers.md](api/timers.md) | +| Read/write the global `Stage` file | [api/stage.md](api/stage.md) | +| Read/write the global `Workspace` file | [api/workspace.md](api/workspace.md) | +| Read/write the global `Props` file | [api/props.md](api/props.md) | +| Read/write the global `TestPatterns` file | [api/test-patterns.md](api/test-patterns.md) | +| Read/write the global `Calendar` file | [api/calendar.md](api/calendar.md) | +| Read/write the global `KeyMappings` file | [api/key-mappings.md](api/key-mappings.md) | +| Read/write the global `CommunicationDevices` JSON file | [api/communication-devices.md](api/communication-devices.md) | +| Read/write theme folders (Theme + Assets/) | [api/theme.md](api/theme.md) | | Understand `.pro` binary format | [formats/pp_song_spec.md](formats/pp_song_spec.md) | | Understand `.proplaylist` format | [formats/pp_playlist_spec.md](formats/pp_playlist_spec.md) | | Understand `.probundle` format | [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md) | @@ -27,11 +40,27 @@ - [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md) — ProPresenter 7 `.probundle` file format (ZIP container, media assets) ### PHP API Documentation +#### Document containers - [api/song.md](api/song.md) — Song parser API (read, modify, generate `.pro` files) - [api/playlist.md](api/playlist.md) — Playlist parser API (read, modify, generate `.proplaylist` files) - [api/bundle.md](api/bundle.md) — Bundle parser API (read, write `.probundle` files with media) -- [api/macros.md](api/macros.md) — Macros library API (read the global `Macros` file) -- [api/labels.md](api/labels.md) — Labels library API (read the global `Labels` file) +- [api/theme.md](api/theme.md) — Theme bundle API (folder with `Theme` proto + `Assets/`) + +#### Global library files (read + write) +- [api/macros.md](api/macros.md) — `Macros` library (macros + collections, with editable accessors) +- [api/labels.md](api/labels.md) — `Labels` library (named slide labels with optional UI colors) +- [api/groups.md](api/groups.md) — `Groups` library (named library groups with UUID, color, hot keys) +- [api/clear-groups.md](api/clear-groups.md) — `ClearGroups` library (clear-action groups) +- [api/ccli.md](api/ccli.md) — `CCLI` settings (license, display behaviour, copyright template) +- [api/messages.md](api/messages.md) — `Messages` library (lower-third / overlay messages) +- [api/timers.md](api/timers.md) — `Timers` library (timer definitions + clock format) +- [api/stage.md](api/stage.md) — `Stage` document (stage display layouts) +- [api/workspace.md](api/workspace.md) — `Workspace` document (screens, looks, masks, audio/video inputs) +- [api/props.md](api/props.md) — `Props` document (prop cues + transition) +- [api/test-patterns.md](api/test-patterns.md) — `TestPatterns` document (currently selected pattern + saved overrides) +- [api/calendar.md](api/calendar.md) — `Calendar` document (scheduled events firing macros) +- [api/key-mappings.md](api/key-mappings.md) — `KeyMappings` document (custom hot-key bindings) +- [api/communication-devices.md](api/communication-devices.md) — `CommunicationDevices` JSON list (MIDI / serial / OSC bindings) ### Internal Reference - [internal/learnings.md](internal/learnings.md) — Development learnings and conventions discovered @@ -59,8 +88,21 @@ doc/ │ ├── song.md │ ├── playlist.md │ ├── bundle.md +│ ├── theme.md │ ├── macros.md -│ └── labels.md +│ ├── labels.md +│ ├── groups.md +│ ├── clear-groups.md +│ ├── ccli.md +│ ├── messages.md +│ ├── timers.md +│ ├── stage.md +│ ├── workspace.md +│ ├── props.md +│ ├── test-patterns.md +│ ├── calendar.md +│ ├── key-mappings.md +│ └── communication-devices.md └── internal/ ← Development notes (optional context) ├── learnings.md ├── decisions.md @@ -86,6 +128,17 @@ Load: doc/api/playlist.md Load: doc/api/bundle.md ``` +### Task: "Edit a global library file (Macros, Labels, Groups, etc.)" +``` +Load: doc/api/.md +``` + +### Task: "Round-trip a Theme folder with assets" +``` +Load: doc/api/theme.md +Load: doc/api/bundle.md (for the bundle pattern reference) +``` + ### Task: "Debug protobuf parsing issues" ``` Load: doc/formats/pp_song_spec.md (sections 2-5) @@ -113,6 +166,8 @@ This project provides PHP tools to parse, modify, and generate ProPresenter 7 fi - **Songs** (`.pro`) — Presentation files containing lyrics with groups, slides, arrangements, and translations - **Playlists** (`.proplaylist`) — ZIP archives containing playlist metadata and embedded song files - **Bundles** (`.probundle`) — ZIP archives containing a single presentation with embedded media assets +- **Global library files** — `Macros`, `Labels`, `Groups`, `ClearGroups`, `CCLI`, `Messages`, `Timers`, `Stage`, `Workspace`, `Props`, `TestPatterns`, `Calendar`, `KeyMappings`, `CommunicationDevices` (JSON) +- **Theme folders** — directory with a `Theme` protobuf file plus an `Assets/` subdirectory of media ### Key Components @@ -129,23 +184,46 @@ This project provides PHP tools to parse, modify, and generate ProPresenter 7 fi | `src/PresentationBundle.php` | Bundle wrapper (read/write `.probundle` files) | | `src/ProBundleReader.php` | Read `.probundle` files | | `src/ProBundleWriter.php` | Write `.probundle` files | -| `src/MacroLibrary.php` | Macros library wrapper (read global `Macros` file) | -| `src/MacrosFileReader.php` | Read global `Macros` file | -| `src/LabelLibrary.php` | Labels library wrapper (read global `Labels` file) | -| `src/LabelsFileReader.php` | Read global `Labels` file | +| `src/ThemeBundle.php` | Theme folder wrapper (Template.Document + Assets/) | +| `src/ThemeFileReader.php` / `ThemeFileWriter.php` | Read/write theme folders | +| `src/MacroLibrary.php` / `MacrosFileReader.php` / `MacrosFileWriter.php` | Macros file IO | +| `src/LabelLibrary.php` / `LabelsFileReader.php` / `LabelsFileWriter.php` | Labels file IO | +| `src/GroupLibrary.php` / `GroupsFileReader.php` / `GroupsFileWriter.php` | Groups file IO | +| `src/ClearGroupsLibrary.php` / `ClearGroupsFileReader.php` / `ClearGroupsFileWriter.php` | ClearGroups file IO | +| `src/CCLILibrary.php` / `CCLIFileReader.php` / `CCLIFileWriter.php` | CCLI file IO | +| `src/MessageLibrary.php` / `MessagesFileReader.php` / `MessagesFileWriter.php` | Messages file IO | +| `src/TimersLibrary.php` / `TimersFileReader.php` / `TimersFileWriter.php` | Timers file IO | +| `src/StageLibrary.php` / `StageFileReader.php` / `StageFileWriter.php` | Stage file IO | +| `src/WorkspaceLibrary.php` / `WorkspaceFileReader.php` / `WorkspaceFileWriter.php` | Workspace file IO | +| `src/PropLibrary.php` / `PropsFileReader.php` / `PropsFileWriter.php` | Props file IO | +| `src/TestPatternsLibrary.php` / `TestPatternsFileReader.php` / `TestPatternsFileWriter.php` | TestPatterns file IO | +| `src/CalendarLibrary.php` / `CalendarFileReader.php` / `CalendarFileWriter.php` | Calendar file IO | +| `src/KeyMappingsLibrary.php` / `KeyMappingsFileReader.php` / `KeyMappingsFileWriter.php` | KeyMappings file IO | +| `src/CommunicationDevicesLibrary.php` / `CommunicationDevicesFileReader.php` / `CommunicationDevicesFileWriter.php` | CommunicationDevices JSON file IO | ### CLI Tools ```bash -# Parse and display song structure +# Songs / playlists / bundles php bin/parse-song.php path/to/song.pro - -# Parse and display playlist structure php bin/parse-playlist.php path/to/playlist.proplaylist -# Parse and display the global Macros file +# Global library files php bin/parse-macros.php path/to/Macros - -# Parse and display the global Labels file php bin/parse-labels.php path/to/Labels +php bin/parse-groups.php path/to/Groups +php bin/parse-clear-groups.php path/to/ClearGroups +php bin/parse-ccli.php path/to/CCLI +php bin/parse-messages.php path/to/Messages +php bin/parse-timers.php path/to/Timers +php bin/parse-stage.php path/to/Stage +php bin/parse-workspace.php path/to/Workspace +php bin/parse-props.php path/to/Props +php bin/parse-test-patterns.php path/to/TestPatterns +php bin/parse-calendar.php path/to/Calendar +php bin/parse-key-mappings.php path/to/KeyMappings +php bin/parse-communication-devices.php path/to/CommunicationDevices + +# Theme folder +php bin/parse-theme.php path/to/ThemeFolder ``` diff --git a/doc/api/calendar.md b/doc/api/calendar.md new file mode 100644 index 0000000..8335012 --- /dev/null +++ b/doc/api/calendar.md @@ -0,0 +1,115 @@ +# Calendar Library API + +> PHP module for reading and writing the global ProPresenter `Calendar` file +> (raw protobuf, no extension) and preserving scheduled macro events. + +## Quick Reference + +```php +use ProPresenter\Parser\CalendarFileReader; + +$library = CalendarFileReader::read('/path/to/Calendar'); + +foreach ($library->getEvents() as $event) { + $event->getName(); + $event->getStartTimeSeconds(); + $event->getActionData(); // raw bytes +} +``` + +--- + +## File Layout + +The `Calendar` file is the protobuf-serialised +[`CalendarDocument`](../../proto/calendar.proto): + +| Field | Type | Description | +|-------|------|-------------| +| `events` | repeated `CalendarDocument.Event` | Scheduled events | +| `mode` | `uint32` | Opaque document mode/source flag | + +Each event includes UUID, name, start/end timestamps, opaque `flags`, and two +raw bytes fields: `action_data` (field 8) and `macro_data` (field 9). Those +bytes are intentionally not decoded by this wrapper. + +--- + +## Reading + +```php +$library = CalendarFileReader::read('/Users/me/.../Calendar'); +``` + +Throws `InvalidArgumentException` for missing files and `RuntimeException` for +empty / unreadable files. + +--- + +## CalendarLibrary + +```php +$library->getEvents(); // CalendarEvent[] +$library->count(); // int +$library->getEventByUuid($uuid); // ?CalendarEvent (case-insensitive) +$library->getEventByName($name); // ?CalendarEvent +$library->addEvent($name, $uuid); // CalendarEvent +$library->removeEvent($uuid); // bool +$library->getMode(); // int +$library->setMode(1); // void +$library->getDocument(); // \Rv\Data\CalendarDocument +``` + +--- + +## CalendarEvent + +```php +$event->getUuid(); +$event->setUuid($uuid); +$event->getName(); +$event->setName($name); +$event->getStartTime(); +$event->setStartTime($timestamp); +$event->getStartTimeSeconds(); +$event->setStartTimeSeconds($seconds); +$event->getEndTime(); +$event->getEndTimeSeconds(); +$event->getFlags(); +$event->setFlags($bytes); +$event->getActionData(); // raw protobuf bytes +$event->setActionData($bytes); +$event->getMacroData(); // raw protobuf bytes +$event->setMacroData($bytes); +$event->getProto(); +``` + +--- + +## CLI Tool + +```bash +php bin/parse-calendar.php /path/to/Calendar +``` + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `src/CalendarLibrary.php` | Document wrapper with UUID / name indexes | +| `src/CalendarEvent.php` | Single event wrapper | +| `src/CalendarFileReader.php` | Reads the `Calendar` file | +| `src/CalendarFileWriter.php` | Writes the `Calendar` file | +| `bin/parse-calendar.php` | CLI summary tool | +| `proto/calendar.proto` | Protobuf schema | +| `generated/Rv/Data/CalendarDocument.php` | Generated document class | + +--- + +## Scope Notes + +`action_data` and `macro_data` are raw protobuf byte strings. They are exposed +directly for byte-preserving edits and round trips; semantic decoding belongs in +a future schema-specific module. diff --git a/doc/api/ccli.md b/doc/api/ccli.md new file mode 100644 index 0000000..7000e7d --- /dev/null +++ b/doc/api/ccli.md @@ -0,0 +1,104 @@ +# CCLI Library API + +> PHP module for reading and writing the global ProPresenter `CCLI` file (raw +> protobuf, no extension) that controls copyright display settings. + +## Quick Reference + +```php +use ProPresenter\Parser\CCLIFileReader; +use ProPresenter\Parser\CCLIFileWriter; + +$library = CCLIFileReader::read('/path/to/CCLI'); + +$library->isCCLIDisplayEnabled(); // bool +$library->getCCLILicense(); // string +$library->getDisplayType(); // int enum value +$library->getTemplate(); // ?\Rv\Data\Template\Slide + +$library->setCCLILicense('1234567'); +CCLIFileWriter::write($library, '/path/to/CCLI'); +``` + +--- + +## File Layout + +The `CCLI` file is the protobuf-serialised +[`CCLIDocument`](../../proto/ccli.proto): + +| Field | Type | Description | +|-------|------|-------------| +| `application_info` | `ApplicationInfo` | ProPresenter writer metadata | +| `enable_ccli_display` | bool | Whether copyright info is shown | +| `ccli_license` | string | CCLI license number | +| `display_type` | `CCLIDocument.DisplayType` | First, last, first+last, or all slides | +| `template` | `Template.Slide` | Text/template styling for display | + +--- + +## Reading + +```php +use ProPresenter\Parser\CCLIFileReader; + +$library = CCLIFileReader::read('/Users/me/.../CCLI'); +``` + +Throws `InvalidArgumentException` for missing files and `RuntimeException` for +empty / unreadable files. + +--- + +## CCLILibrary + +Top-level wrapper around `Rv\Data\CCLIDocument`. This is a single-document +configuration file; `count()` returns `1` when read successfully. + +```php +$library->count(); +$library->isCCLIDisplayEnabled(); +$library->setCCLIDisplayEnabled(true); +$library->getCCLILicense(); +$library->setCCLILicense('1234567'); +$library->getDisplayType(); +$library->setDisplayType(3); +$library->getTemplate(); +$library->setTemplate($slideOrNull); +$library->getDocument(); // \Rv\Data\CCLIDocument +``` + +--- + +## CLI Tool + +```bash +php bin/parse-ccli.php /path/to/CCLI +``` + +Output: + +``` +CCLI (1): + [1] enabled=yes :: license=(empty) :: display_type=0 :: template=yes +``` + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `src/CCLILibrary.php` | Document-level wrapper | +| `src/CCLIFileReader.php` | Reads the `CCLI` file | +| `src/CCLIFileWriter.php` | Writes the `CCLI` file | +| `bin/parse-ccli.php` | CLI tool | +| `proto/ccli.proto` | Protobuf schema | +| `generated/Rv/Data/CCLIDocument.php` | Generated message class | + +--- + +## Scope Notes + +The wrapper preserves template data and application metadata by mutating the +generated protobuf in place. It does not inspect or render the slide template. diff --git a/doc/api/clear-groups.md b/doc/api/clear-groups.md new file mode 100644 index 0000000..c46fc67 --- /dev/null +++ b/doc/api/clear-groups.md @@ -0,0 +1,135 @@ +# ClearGroups Library API + +> PHP module for reading and writing the global ProPresenter `ClearGroups` file +> (raw protobuf, no extension) and exposing each clear group definition. + +## Quick Reference + +```php +use ProPresenter\Parser\ClearGroupsFileReader; +use ProPresenter\Parser\ClearGroupsFileWriter; + +$library = ClearGroupsFileReader::read('/path/to/ClearGroups'); + +foreach ($library->getGroups() as $group) { + $group->getName(); // "Alles ausblenden" + $group->getUuid(); // "A91C6AFE-..." + $group->getImageType(); // 11 + $group->getColorHex(); // "#FFFFFF" | null +} + +ClearGroupsFileWriter::write($library, '/path/to/ClearGroups'); +``` + +--- + +## File Layout + +The `ClearGroups` file is the protobuf-serialised +[`ClearGroupsDocument`](../../proto/clearGroups.proto): + +| Field | Type | Description | +|-------|------|-------------| +| `application_info` | `ApplicationInfo` | ProPresenter writer metadata | +| `groups` | repeated `ClearGroupsDocument.ClearGroup` | Clear button definitions | + +Each `ClearGroup` carries: + +| Field | Type | Description | +|-------|------|-------------| +| `uuid` | `UUID` | Stable identifier | +| `name` | string | Display name | +| `layer_targets` | repeated `Action.ClearType` | Layers cleared by the button | +| `is_hidden_in_preview` | bool | Whether preview UI hides the button | +| `image_data` | bytes | Custom icon payload | +| `image_type` | enum | Built-in icon identifier | +| `is_icon_tinted` | bool | Whether `icon_tint_color` applies | +| `icon_tint_color` | `Color` | RGBA float channels in 0..1 | +| `timeline_targets` | repeated `Action.ContentDestination` | Timeline destinations | +| `clear_presentation_next_slide` | bool | Also clear the queued next slide | + +--- + +## Reading + +```php +use ProPresenter\Parser\ClearGroupsFileReader; + +$library = ClearGroupsFileReader::read('/Users/me/.../ClearGroups'); +``` + +Throws `InvalidArgumentException` for missing files and `RuntimeException` for +empty / unreadable files. + +--- + +## ClearGroupsLibrary + +Top-level wrapper around `Rv\Data\ClearGroupsDocument`. Indexes groups by UUID +(case-insensitive) and name. + +```php +$library->getGroups(); // ClearGroupDefinition[] +$library->count(); // int +$library->getClearGroupByUuid('A91C...'); // ?ClearGroupDefinition +$library->getClearGroupByName('Alles ...'); // ?ClearGroupDefinition +$library->addClearGroup('Name', 'UUID'); // ClearGroupDefinition +$library->removeClearGroup('UUID'); // bool +$library->getDocument(); // \Rv\Data\ClearGroupsDocument +``` + +--- + +## ClearGroupDefinition + +```php +$group->getName(); +$group->setName('New Name'); +$group->getUuid(); +$group->setUuid('...'); +$group->getLayerTargets(); +$group->getImageType(); +$group->getColor(); // ['r'=>..,'g'=>..,'b'=>..,'a'=>..] | null +$group->getColorHex(); // "#RRGGBB" uppercase, alpha dropped, or null +$group->getProto(); // \Rv\Data\ClearGroupsDocument\ClearGroup +``` + +Color channels are floats in 0..1 as ProPresenter stores them. `getColorHex()` +clamps and rounds each channel to 8 bits before formatting. + +--- + +## CLI Tool + +```bash +php bin/parse-clear-groups.php /path/to/ClearGroups +``` + +Output: + +``` +ClearGroups (1): + [1] Alles ausblenden :: A91C6AFE-098F-4559-B2CF-D8373C589589 :: image_type=11 :: #FFFFFF +``` + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `src/ClearGroupsLibrary.php` | Document-level wrapper with UUID/name lookups | +| `src/ClearGroupDefinition.php` | Single clear group wrapper | +| `src/ClearGroupsFileReader.php` | Reads the `ClearGroups` file | +| `src/ClearGroupsFileWriter.php` | Writes the `ClearGroups` file | +| `bin/parse-clear-groups.php` | CLI tool | +| `proto/clearGroups.proto` | Protobuf schema | +| `generated/Rv/Data/ClearGroupsDocument.php` | Generated message class | + +--- + +## Scope Notes + +The wrapper preserves unknown / uninterpreted protobuf data by mutating the +generated message in place and serialising it back. It does not execute clear +actions or modify slide content. diff --git a/doc/api/communication-devices.md b/doc/api/communication-devices.md new file mode 100644 index 0000000..9504080 --- /dev/null +++ b/doc/api/communication-devices.md @@ -0,0 +1,96 @@ +# CommunicationDevices Library API + +> PHP module for reading and writing the global ProPresenter +> `CommunicationDevices` file. Unlike most config files in this project, this +> file is JSON, not protobuf. + +## Quick Reference + +```php +use ProPresenter\Parser\CommunicationDevice; +use ProPresenter\Parser\CommunicationDevicesFileReader; +use ProPresenter\Parser\CommunicationDevicesFileWriter; + +$library = CommunicationDevicesFileReader::read('/path/to/CommunicationDevices'); +$library->addDevice((new CommunicationDevice())->setId('device-1')->setName('Router')); +CommunicationDevicesFileWriter::write($library, '/path/to/CommunicationDevices'); +``` + +--- + +## File Layout + +`CommunicationDevices` is a JSON array. The reference sample is `[]`, so the +wrapper preserves arbitrary object fields while exposing forward-looking common +fields: `id`, `name`, `type`, and `address`. + +--- + +## Reading + +```php +$library = CommunicationDevicesFileReader::read('/Users/me/.../CommunicationDevices'); +``` + +Throws `InvalidArgumentException` for missing files and `RuntimeException` for +unreadable files or invalid JSON. + +--- + +## CommunicationDevicesLibrary + +```php +CommunicationDevicesLibrary::fromJson($json); // CommunicationDevicesLibrary +$library->toJson(); // string +$library->getDocument(); // raw decoded array +$library->getDevices(); // CommunicationDevice[] +$library->addDevice($device); // CommunicationDevice +$library->removeDevice($id); // bool +$library->count(); // int +``` + +--- + +## CommunicationDevice + +```php +$device->getId(); +$device->setId($id); +$device->getName(); +$device->setName($name); +$device->getType(); +$device->setType($type); +$device->getAddress(); +$device->setAddress($address); +$device->toArray(); // full decoded JSON object +``` + +--- + +## CLI Tool + +```bash +php bin/parse-communication-devices.php /path/to/CommunicationDevices +``` + +Empty files print a useful `(none configured)` summary. + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `src/CommunicationDevicesLibrary.php` | JSON-array wrapper | +| `src/CommunicationDevice.php` | Single JSON device value object | +| `src/CommunicationDevicesFileReader.php` | Reads and validates JSON | +| `src/CommunicationDevicesFileWriter.php` | Writes compact JSON | +| `bin/parse-communication-devices.php` | CLI summary tool | + +--- + +## Scope Notes + +Because only an empty sample is available, unknown JSON fields are preserved in +each device's backing array. The writer uses compact JSON with unescaped +slashes / Unicode for stable semantic round trips. diff --git a/doc/api/groups.md b/doc/api/groups.md new file mode 100644 index 0000000..d6a13f5 --- /dev/null +++ b/doc/api/groups.md @@ -0,0 +1,172 @@ +# 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. diff --git a/doc/api/key-mappings.md b/doc/api/key-mappings.md new file mode 100644 index 0000000..bf57ade --- /dev/null +++ b/doc/api/key-mappings.md @@ -0,0 +1,130 @@ +# KeyMappings Library API + +> PHP module for reading and writing the global ProPresenter `KeyMappings` file +> (raw protobuf, no extension) and exposing configured hot-key mappings. + +## Quick Reference + +```php +use ProPresenter\Parser\KeyMappingsFileReader; +use ProPresenter\Parser\KeyMappingsFileWriter; + +$library = KeyMappingsFileReader::read('/path/to/KeyMappings'); + +foreach ($library->getMappings() as $mapping) { + $mapping->getName(); // string + $mapping->getUuid(); // string + $mapping->getHotKey(); // ?\Rv\Data\HotKey + $mapping->getTarget(); // raw bytes +} + +KeyMappingsFileWriter::write($library, '/path/to/KeyMappings'); +``` + +--- + +## File Layout + +The `KeyMappings` file is the protobuf-serialised +[`KeyMappingsDocument`](../../proto/keyMappings.proto): + +| Field | Type | Description | +|-------|------|-------------| +| `application_info` | `ApplicationInfo` | ProPresenter writer metadata | +| `mappings` | repeated `KeyMappingsDocument.Mapping` | Configured key bindings | + +Each `Mapping` carries: + +| Field | Type | Description | +|-------|------|-------------| +| `uuid` | `UUID` | Optional stable mapping identifier | +| `hot_key` | `HotKey` | Key combo that fires the action | +| `target` | bytes | Raw target reference | +| `name` | string | Optional display name | + +--- + +## Reading + +```php +use ProPresenter\Parser\KeyMappingsFileReader; + +$library = KeyMappingsFileReader::read('/Users/me/.../KeyMappings'); +``` + +Throws `InvalidArgumentException` for missing files and `RuntimeException` for +empty / unreadable files. + +--- + +## KeyMappingsLibrary + +Top-level wrapper around `Rv\Data\KeyMappingsDocument`. Indexes mappings by UUID +(case-insensitive) and name. + +```php +$library->getMappings(); +$library->count(); +$library->getMappingByUuid('...'); +$library->getMappingByName('Macro trigger'); +$library->addMapping('Macro trigger', 'UUID', $targetBytes); +$library->removeMapping('UUID'); +$library->getApplicationInfo(); +$library->setApplicationInfo($infoOrNull); +$library->getDocument(); // \Rv\Data\KeyMappingsDocument +``` + +--- + +## KeyMapping + +```php +$mapping->getName(); +$mapping->setName('New Name'); +$mapping->getUuid(); +$mapping->setUuid('...'); +$mapping->getHotKey(); +$mapping->setHotKey($hotKeyOrNull); +$mapping->getTarget(); +$mapping->setTarget($bytes); +$mapping->getProto(); // \Rv\Data\KeyMappingsDocument\Mapping +``` + +Targets are raw bytes because ProPresenter may encode several internal target +types here. Keeping bytes opaque preserves round-trip safety. + +--- + +## CLI Tool + +```bash +php bin/parse-key-mappings.php /path/to/KeyMappings +``` + +Output for the reference sample: + +``` +KeyMappings (0): +``` + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `src/KeyMappingsLibrary.php` | Document-level wrapper with UUID/name lookups | +| `src/KeyMapping.php` | Single mapping wrapper | +| `src/KeyMappingsFileReader.php` | Reads the `KeyMappings` file | +| `src/KeyMappingsFileWriter.php` | Writes the `KeyMappings` file | +| `bin/parse-key-mappings.php` | CLI tool | +| `proto/keyMappings.proto` | Protobuf schema | +| `generated/Rv/Data/KeyMappingsDocument.php` | Generated message class | + +--- + +## Scope Notes + +The reference sample contains only `ApplicationInfo` and no mappings. The API +still supports mapping additions/removals so configured user files can be edited +and round-tripped safely. diff --git a/doc/api/labels.md b/doc/api/labels.md index 5059a06..b3e28ec 100644 --- a/doc/api/labels.md +++ b/doc/api/labels.md @@ -7,6 +7,7 @@ ```php use ProPresenter\Parser\LabelsFileReader; +use ProPresenter\Parser\LabelsFileWriter; $library = LabelsFileReader::read('/path/to/Labels'); @@ -16,6 +17,14 @@ foreach ($library->getLabels() as $label) { $label->getColor(); // ['r'=>0.0,'g'=>0.408,'b'=>0.702,'a'=>1.0] | null $label->getColorHex(); // "#0068B3" | null } + +// Modify and persist +$library->addLabel('NewLabel', ['r' => 1.0, 'g' => 0.5, 'b' => 0.0]); +$beamer = $library->getLabelByName('KeyVisual Beamer'); +$beamer?->setColorHex('#FF8800'); +$library->removeLabel('Wiederholen'); + +LabelsFileWriter::write($library, '/path/to/Labels'); ``` --- @@ -54,6 +63,19 @@ empty / unreadable files. --- +## Writing + +```php +use ProPresenter\Parser\LabelsFileWriter; + +LabelsFileWriter::write($library, '/Users/me/.../Labels'); +``` + +Serialises the underlying `ProLabelsDocument` to bytes. The unmodified +reference sample round-trips byte-for-byte. + +--- + ## LabelLibrary Top-level wrapper around `Rv\Data\ProLabelsDocument`. Indexes labels by name @@ -64,7 +86,10 @@ $library->getLabels(); // Label[] $library->count(); // int $library->getLabelByName('Szene 1'); // ?Label (case-sensitive) $library->findLabelByName('szene 1'); // ?Label (case-insensitive) -$library->getDocument(); // \Rv\Data\ProLabelsDocument + +$library->addLabel('NewLabel', ['r'=>1, 'g'=>0, 'b'=>0]); // ?Label +$library->removeLabel('OldLabel'); // bool +$library->getDocument(); // \Rv\Data\ProLabelsDocument ``` If the same name appears more than once in the source document the first @@ -76,11 +101,15 @@ occurrence wins for both lookup helpers; every entry is preserved in ## Label ```php -$label->getName(); // "KeyVisual Beamer" (proto field is `text`) -$label->hasColor(); // bool — was a Color message present? -$label->getColor(); // ['r'=>..,'g'=>..,'b'=>..,'a'=>..] | null -$label->getColorHex(); // "#RRGGBB" uppercase, alpha dropped, or null -$label->getProto(); // \Rv\Data\Action\Label (raw protobuf) +$label->getName(); // "KeyVisual Beamer" (proto field is `text`) +$label->setName('Renamed'); // self +$label->hasColor(); // bool — was a Color message present? +$label->getColor(); // ['r'=>..,'g'=>..,'b'=>..,'a'=>..] | null +$label->getColorHex(); // "#RRGGBB" uppercase, alpha dropped, or null +$label->setColor(['r'=>1, 'g'=>0, 'b'=>0]); // self +$label->setColor(null); // clears the color (UI falls back to default) +$label->setColorHex('#FF8800'); // accepts #RRGGBB or #RRGGBBAA +$label->getProto(); // \Rv\Data\Action\Label (raw protobuf) ``` Color channels are floats in 0..1 as ProPresenter stores them. `getColorHex()` @@ -118,9 +147,10 @@ Labels (15): | File | Purpose | |------|---------| -| `src/LabelLibrary.php` | Document-level wrapper with name lookups | -| `src/Label.php` | Single label wrapper (name, color, hex) | +| `src/LabelLibrary.php` | Document-level wrapper with name lookups + add / remove helpers | +| `src/Label.php` | Single label wrapper (name, color, hex) with setters | | `src/LabelsFileReader.php` | Reads the `Labels` file | +| `src/LabelsFileWriter.php` | Writes the `Labels` file | | `bin/parse-labels.php` | CLI tool | | `proto/labels.proto` | Protobuf schema (just imports `Action.Label`) | | `proto/action.proto` | Defines the inner `Action.Label` message | @@ -131,7 +161,6 @@ Labels (15): ## Scope Notes -This module is read-only by design. Writing the `Labels` file back, editing -slide-side label references on `.pro` files, or syncing labels across devices -are not implemented here. Add them by mirroring the `ProFileWriter` / -`ProFileGenerator` pattern when needed. +Editing slide-side label references on `.pro` files (cross-document fan-out) +and syncing labels across devices are out of scope; this module only covers +the global `Labels` document. diff --git a/doc/api/macros.md b/doc/api/macros.md index 6415d56..638e392 100644 --- a/doc/api/macros.md +++ b/doc/api/macros.md @@ -8,18 +8,27 @@ ```php use ProPresenter\Parser\MacrosFileReader; +use ProPresenter\Parser\MacrosFileWriter; $library = MacrosFileReader::read('/path/to/Macros'); foreach ($library->getMacros() as $macro) { - $macro->getName(); // "Gottesdienst START" - $macro->getUuid(); // "FA0602E4-EDA2-4457-BB62-68AA17184217" + $macro->getName(); // "Gottesdienst START" + $macro->getUuid(); // "FA0602E4-EDA2-4457-BB62-68AA17184217" + $macro->getColor(); // ['r'=>..,'g'=>..,'b'=>..,'a'=>..] | null + $macro->getImageType(); // int — see ImageType enum + $macro->getImageData(); // bytes — custom icon (empty for built-ins) foreach ($library->getCollectionsForMacro($macro) as $collection) { - $collection->getName(); // "Ablauf" - $collection->getUuid(); // "8D02FC57-83F8-4042-9B90-81C229728426" + $collection->getName(); + $collection->getUuid(); } } + +// Modify and persist +$library->addMacro('NewMacro', '...uuid...'); +$library->getMacroByName('NewMacro')?->setColor(['r'=>0.5, 'g'=>0, 'b'=>1]); +MacrosFileWriter::write($library, '/path/to/Macros'); ``` --- @@ -53,6 +62,20 @@ empty / unreadable files. --- +## Writing + +```php +use ProPresenter\Parser\MacrosFileWriter; + +MacrosFileWriter::write($library, '/Users/me/.../Macros'); +``` + +Serialises the underlying `MacrosDocument` to bytes. Round-trip preserves the +overall byte length; field ordering can vary slightly because the protobuf +PHP runtime is not guaranteed to be canonical. + +--- + ## MacroLibrary Top-level wrapper around `Rv\Data\MacrosDocument`. Indexes macros and @@ -71,6 +94,12 @@ $library->getCollectionByName('Ablauf'); // ?MacroCollection $library->getMacrosForCollection($collection); // Macro[] in declared order $library->getCollectionsForMacro($macro); // MacroCollection[] (membership) +// Mutators +$library->addMacro('NewMacro', '...uuid...'); // Macro +$library->removeMacro('...uuid...'); // bool +$library->addCollection('NewCollection', '...uuid...'); // MacroCollection +$library->removeCollection('...uuid...'); // bool + $library->getDocument(); // \Rv\Data\MacrosDocument (raw protobuf) ``` @@ -80,11 +109,18 @@ $library->getDocument(); // \Rv\Data\MacrosDocument (raw protobuf) ```php $macro->getUuid(); // "FA0602E4-..." +$macro->setUuid('...'); // self $macro->getName(); // "Gottesdienst START" +$macro->setName('...'); // self $macro->getColor(); // ['r'=>..,'g'=>..,'b'=>..,'a'=>..] | null +$macro->setColor(['r'=>0.5,'g'=>0,'b'=>1]); // self (or null to clear) $macro->getTriggerOnStartup(); // bool +$macro->setTriggerOnStartup(true); // self $macro->getActionCount(); // int — number of attached Action entries $macro->getImageType(); // int — see Rv\Data\MacrosDocument\Macro\ImageType +$macro->setImageType(...); // self — pass an ImageType enum value +$macro->getImageData(); // string — custom icon bytes (empty for built-ins) +$macro->setImageData($pngBytes); // self — set a custom icon $macro->getProto(); // \Rv\Data\MacrosDocument\Macro ``` @@ -96,10 +132,14 @@ walk `getActions()` directly when needed. ## MacroCollection ```php -$collection->getUuid(); // "8D02FC57-..." -$collection->getName(); // "Ablauf" -$collection->getMacroUuids(); // string[] — referenced macro UUIDs in order -$collection->getProto(); // \Rv\Data\MacrosDocument\MacroCollection +$collection->getUuid(); // "8D02FC57-..." +$collection->setUuid('...'); // self +$collection->getName(); // "Ablauf" +$collection->setName('...'); // self +$collection->getMacroUuids(); // string[] — referenced macro UUIDs in order +$collection->setMacroUuids(['...']); // self — replace all referenced UUIDs +$collection->addMacroUuid('...'); // self — append a single reference +$collection->getProto(); // \Rv\Data\MacrosDocument\MacroCollection ``` Items use a protobuf `oneof ItemType`; only `macro_id` is currently defined. @@ -132,10 +172,11 @@ Collections (3): | File | Purpose | |------|---------| -| `src/MacroLibrary.php` | Document-level wrapper with lookup helpers | -| `src/Macro.php` | Single macro wrapper | -| `src/MacroCollection.php` | Collection wrapper | +| `src/MacroLibrary.php` | Document-level wrapper with lookup + add / remove helpers | +| `src/Macro.php` | Single macro wrapper with setters | +| `src/MacroCollection.php` | Collection wrapper with setters | | `src/MacrosFileReader.php` | Reads the `Macros` file | +| `src/MacrosFileWriter.php` | Writes the `Macros` file | | `bin/parse-macros.php` | CLI tool | | `proto/macros.proto` | Protobuf schema | | `generated/Rv/Data/MacrosDocument.php` | Generated message classes | @@ -144,8 +185,8 @@ Collections (3): ## Scope Notes -This module is read-only by design. Action editing, slide-side macro -references on `.pro` files (see `Slide::getMacroUuid()` / -`Slide::setMacro()`), and writing the `Macros` file back are not implemented -here. Add them by mirroring the `ProFileWriter` / `ProFileGenerator` pattern -when needed. +Action editing (the inner `repeated Action actions` field on each macro) and +slide-side macro references on `.pro` files (see `Slide::getMacroUuid()` / +`Slide::setMacro()`) are out of scope. This module covers the global +`Macros` document only; reach for `getProto()->getActions()` for raw action +inspection. diff --git a/doc/api/messages.md b/doc/api/messages.md new file mode 100644 index 0000000..3195952 --- /dev/null +++ b/doc/api/messages.md @@ -0,0 +1,110 @@ +# Messages Library API + +> PHP module for reading and writing the global ProPresenter `Messages` file +> (raw protobuf, no extension) and exposing each message definition. + +## Quick Reference + +```php +use ProPresenter\Parser\MessagesFileReader; +use ProPresenter\Parser\MessagesFileWriter; + +$library = MessagesFileReader::read('/path/to/Messages'); + +foreach ($library->getMessages() as $message) { + $message->getTitle(); + $message->getUuid(); + $message->getMessageText(); +} + +$library->addMessage('Lobby Notice', '11111111-1111-1111-1111-111111111111'); +MessagesFileWriter::write($library, '/path/to/Messages'); +``` + +--- + +## File Layout + +The `Messages` file is the protobuf-serialised +[`MessageDocument`](../../proto/messages.proto): + +| Field | Type | Description | +|-------|------|-------------| +| `application_info` | `ApplicationInfo` | ProPresenter metadata | +| `messages` | repeated `Message` | Message definitions in document order | + +--- + +## Reading + +```php +$library = MessagesFileReader::read('/Users/me/.../Messages'); +``` + +Throws `InvalidArgumentException` for missing files and `RuntimeException` for +empty / unreadable files. + +--- + +## MessageLibrary + +```php +$library->getMessages(); // Message[] +$library->count(); // int +$library->getMessageByUuid($uuid); // ?Message (case-insensitive) +$library->getMessageByName($title); // ?Message (case-sensitive title) +$library->addMessage($title, $uuid); // Message +$library->removeMessage($uuid); // bool +$library->getApplicationInfo(); // ?\Rv\Data\ApplicationInfo +$library->getDocument(); // \Rv\Data\MessageDocument +``` + +--- + +## Message + +```php +$message->getUuid(); +$message->setUuid($uuid); +$message->getTitle(); +$message->setTitle($title); +$message->getTimeToRemove(); +$message->setTimeToRemove($seconds); +$message->isVisibleOnNetwork(); +$message->setVisibleOnNetwork(true); +$message->getMessageText(); +$message->setMessageText($text); +$message->getClearType(); +$message->setClearType($enumValue); +$message->getTokens(); // raw repeated Token protos +$message->getProto(); // \Rv\Data\Message +``` + +--- + +## CLI Tool + +```bash +php bin/parse-messages.php /path/to/Messages +``` + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `src/MessageLibrary.php` | Document wrapper with title / UUID indexes | +| `src/Message.php` | Single message wrapper | +| `src/MessagesFileReader.php` | Reads the `Messages` file | +| `src/MessagesFileWriter.php` | Writes the `Messages` file | +| `bin/parse-messages.php` | CLI summary tool | +| `generated/Rv/Data/MessageDocument.php` | Generated document class | + +--- + +## Scope Notes + +Tokens and token values are preserved as raw generated protobuf objects. The +wrapper exposes them for advanced callers but does not interpret template +rendering semantics. diff --git a/doc/api/props.md b/doc/api/props.md new file mode 100644 index 0000000..7508639 --- /dev/null +++ b/doc/api/props.md @@ -0,0 +1,80 @@ +# Props Library API + +> PHP module for reading, modifying, and writing the global ProPresenter `Props` file. + +## Quick Reference + +```php +use ProPresenter\Parser\PropsFileReader; +use ProPresenter\Parser\PropsFileWriter; + +$library = PropsFileReader::read('/path/to/Props'); + +foreach ($library->getProps() as $prop) { + $prop->getName(); + $prop->getUuid(); + $prop->isEnabled(); +} + +PropsFileWriter::write($library, '/path/to/Props'); +``` + +--- + +## File Layout + +The `Props` file is `Rv\Data\PropDocument` from `propDocument.proto`. +Each prop is stored as a `Rv\Data\Cue` in the document's `cues` field. + +--- + +## PropLibrary + +```php +$library->getDocument(); +$library->getProps(); +$library->getPropByUuid('1FB2...'); // case-insensitive +$library->getPropByName('Props #1'); +$library->addProp($prop); +$library->removeProp($uuid); +$library->count(); +$library->getApplicationInfo(); +``` + +--- + +## Prop + +```php +$prop->getName(); +$prop->setName('Lower Third'); +$prop->getUuid(); +$prop->setUuid('...'); +$prop->isEnabled(); +$prop->setEnabled(true); +$prop->getCompletionTime(); +$prop->getActions(); +$prop->getProto(); +``` + +Use `getProto()` for full Cue/action access. + +--- + +## CLI Tool + +```bash +php bin/parse-props.php /path/to/Props +``` + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `src/PropsFileReader.php` | Reads the `Props` file | +| `src/PropsFileWriter.php` | Writes the `Props` file | +| `src/PropLibrary.php` | Document wrapper and indexes | +| `src/Prop.php` | Single Cue/prop wrapper | +| `bin/parse-props.php` | CLI summary tool | diff --git a/doc/api/stage.md b/doc/api/stage.md new file mode 100644 index 0000000..d5aa535 --- /dev/null +++ b/doc/api/stage.md @@ -0,0 +1,83 @@ +# Stage Library API + +> PHP module for reading, modifying, and writing the global ProPresenter `Stage` file. + +## Quick Reference + +```php +use ProPresenter\Parser\StageFileReader; +use ProPresenter\Parser\StageFileWriter; + +$library = StageFileReader::read('/path/to/Stage'); + +foreach ($library->getLayouts() as $layout) { + $layout->getName(); + $layout->getUuid(); + $layout->getSlide(); // ?\Rv\Data\Slide +} + +StageFileWriter::write($library, '/path/to/Stage'); +``` + +--- + +## File Layout + +The `Stage` file is the protobuf-serialised `Rv\Data\Stage\Document` from +`stage.proto`. + +| Field | Type | Description | +|-------|------|-------------| +| `application_info` | `ApplicationInfo` | ProPresenter metadata | +| `layouts` | repeated `Stage.Layout` | Stage display layouts | + +--- + +## StageLibrary + +```php +$library->getDocument(); +$library->getLayouts(); +$library->getLayoutByUuid('0455...'); // case-insensitive +$library->getLayoutByName('Default StageDisplay'); +$library->addLayout($layout); +$library->removeLayout($uuid); +$library->count(); +$library->getApplicationInfo(); +``` + +--- + +## StageLayout + +```php +$layout->getName(); +$layout->setName('New name'); +$layout->getUuid(); +$layout->setUuid('...'); +$layout->getSlide(); +$layout->getProto(); +``` + +The slide is exposed as the raw `Rv\Data\Slide` protobuf because stage layouts +can contain complex arrangements. + +--- + +## CLI Tool + +```bash +php bin/parse-stage.php /path/to/Stage +``` + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `src/StageFileReader.php` | Reads the `Stage` file | +| `src/StageFileWriter.php` | Writes the `Stage` file | +| `src/StageLibrary.php` | Document wrapper and indexes | +| `src/StageLayout.php` | Single layout wrapper | +| `bin/parse-stage.php` | CLI summary tool | diff --git a/doc/api/test-patterns.md b/doc/api/test-patterns.md new file mode 100644 index 0000000..ca4f6ed --- /dev/null +++ b/doc/api/test-patterns.md @@ -0,0 +1,106 @@ +# TestPatterns Library API + +> PHP module for reading and writing the global ProPresenter `TestPatterns` file +> (raw protobuf, no extension), including selected test-pattern state and saved +> pattern definitions. + +## Quick Reference + +```php +use ProPresenter\Parser\TestPatternsFileReader; +use ProPresenter\Parser\TestPatternsFileWriter; + +$library = TestPatternsFileReader::read('/path/to/TestPatterns'); + +$library->getDisplayLocation(); // 3 +$library->getSpecificScreenUuid(); // "BCDE1115-..." +$library->getPatterns(); // TestPatternData[] + +TestPatternsFileWriter::write($library, '/path/to/TestPatterns'); +``` + +--- + +## File Layout + +The `TestPatterns` file is the protobuf-serialised +[`TestPatternDocument`](../../proto/testPattern.proto): + +| Field | Type | Description | +|-------|------|-------------| +| `state` | `TestPatternDocument.TestPatternStateData` | Current test-pattern display state | +| `patterns` | repeated `TestPatternDocument.TestPatternData` | Saved pattern definitions | + +`TestPatternStateData` includes selected pattern UUID/name, display location, +specific screen UUID, identify-screen flag, logo type, and optional user logo. + +--- + +## Reading + +```php +use ProPresenter\Parser\TestPatternsFileReader; + +$library = TestPatternsFileReader::read('/Users/me/.../TestPatterns'); +``` + +Throws `InvalidArgumentException` for missing files and `RuntimeException` for +empty / unreadable files. + +--- + +## TestPatternsLibrary + +Top-level wrapper around `Rv\Data\TestPatternDocument`. Indexes saved pattern +definitions by UUID (case-insensitive) and localization key. + +```php +$library->getState(); +$library->setState($stateOrNull); +$library->getSelectedPatternUuid(); +$library->getSelectedPatternNameLocalizationKey(); +$library->getDisplayLocation(); +$library->getSpecificScreenUuid(); +$library->getPatterns(); +$library->count(); +$library->getPatternByUuid('...'); +$library->getPatternByName('Test Pattern'); +$library->addPattern('Test Pattern', 'UUID'); +$library->removePattern('UUID'); +$library->getDocument(); // \Rv\Data\TestPatternDocument +``` + +--- + +## CLI Tool + +```bash +php bin/parse-test-patterns.php /path/to/TestPatterns +``` + +Output: + +``` +TestPatterns (0): + State: selected=(none) :: name=(none) :: display_location=3 :: screen=BCDE1115-AD40-4BA4-A33A-BFFE3E87223B +``` + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `src/TestPatternsLibrary.php` | Document-level wrapper with state accessors | +| `src/TestPatternsFileReader.php` | Reads the `TestPatterns` file | +| `src/TestPatternsFileWriter.php` | Writes the `TestPatterns` file | +| `bin/parse-test-patterns.php` | CLI tool | +| `proto/testPattern.proto` | Protobuf schema | +| `generated/Rv/Data/TestPatternDocument.php` | Generated message class | + +--- + +## Scope Notes + +The wrapper exposes `TestPatternData` and `TestPatternStateData` protobufs +directly. It does not render test patterns or interpret nested property oneofs. diff --git a/doc/api/theme.md b/doc/api/theme.md new file mode 100644 index 0000000..fea4e22 --- /dev/null +++ b/doc/api/theme.md @@ -0,0 +1,116 @@ +# Theme Bundle API + +> PHP module for reading, modifying, and writing folder-based ProPresenter themes. + +## Quick Reference + +```php +use ProPresenter\Parser\ThemeFileReader; +use ProPresenter\Parser\ThemeFileWriter; + +$theme = ThemeFileReader::read('/path/to/theme-folder'); + +foreach ($theme->getSlides() as $slide) { + $slide->getName(); // KeyVisual, Liedtext, ... + $slide->getBaseSlide(); // ?\Rv\Data\Slide +} + +foreach ($theme->getAssets() as $asset) { + $asset->getName(); + $asset->getSize(); + $asset->getMimeType(); +} + +ThemeFileWriter::write($theme, '/path/to/output-folder'); +``` + +--- + +## Folder Layout + +A theme is a directory, not a single protobuf file: + +```text +SampleTheme/ +├── Theme # Rv\Data\Template\Document protobuf +└── Assets/ + ├── BACKGROUND.jpg + ├── BAUCHBIND_STREAM.jpg + └── KEY_VISUAL.jpg +``` + +The `Theme` file is a `Rv\Data\Template\Document` from `template.proto`. +Its slides are named theme layouts. + +--- + +## ThemeBundle + +```php +$theme->getDocument(); +$theme->getSlides(); +$theme->getSlideByName('KeyVisual'); +$theme->addSlide($slide); +$theme->removeSlide('KeyVisual'); +$theme->getAssets(); +$theme->getAssetByName('BACKGROUND.jpg'); +$theme->addAsset('NEW.jpg', $bytes); +$theme->removeAsset('NEW.jpg'); +$theme->count(); +$theme->getAssetCount(); +``` + +--- + +## ThemeSlide + +```php +$slide->getName(); +$slide->setName('Liedtext'); +$slide->getBaseSlide(); +$slide->getProto(); +``` + +--- + +## ThemeAsset + +```php +$asset->getName(); +$asset->getBytes(); +$asset->getSize(); +$asset->getMimeType(); // image/jpeg, image/png, ... +``` + +MIME type detection is extension-based and best-effort. + +--- + +## Writing Themes + +`ThemeFileWriter::write()` creates the target folder if needed, writes the +serialized `Theme` protobuf, creates `Assets/`, writes every `ThemeAsset`, and +removes stale files from `Assets/` that are not present in the bundle. + +--- + +## CLI Tool + +```bash +php bin/parse-theme.php /path/to/theme-folder +``` + +The CLI prints slide names plus asset filenames, sizes, and MIME types. + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `src/ThemeBundle.php` | Top-level theme wrapper | +| `src/ThemeFileReader.php` | Reads a theme folder | +| `src/ThemeFileWriter.php` | Writes a theme folder and cleans stale assets | +| `src/ThemeSlide.php` | Single template slide wrapper | +| `src/ThemeAsset.php` | Single asset value object | +| `bin/parse-theme.php` | CLI summary tool | diff --git a/doc/api/timers.md b/doc/api/timers.md new file mode 100644 index 0000000..c2062f6 --- /dev/null +++ b/doc/api/timers.md @@ -0,0 +1,106 @@ +# Timers Library API + +> PHP module for reading and writing the global ProPresenter `Timers` file +> (raw protobuf, no extension), including the top-level clock format. + +## Quick Reference + +```php +use ProPresenter\Parser\TimersFileReader; + +$library = TimersFileReader::read('/path/to/Timers'); +$library->getClockFormat(); // "HH:mm" + +foreach ($library->getTimers() as $timer) { + $timer->getName(); + $timer->isCountdown(); + $timer->getDurationSeconds(); +} +``` + +--- + +## File Layout + +The `Timers` file is the protobuf-serialised +[`TimersDocument`](../../proto/timers.proto): + +| Field | Type | Description | +|-------|------|-------------| +| `application_info` | `ApplicationInfo` | ProPresenter metadata | +| `clock` | `Clock` | Global clock display settings | +| `timers` | repeated `Timer` | Timer definitions | + +--- + +## Reading + +```php +$library = TimersFileReader::read('/Users/me/.../Timers'); +``` + +Throws `InvalidArgumentException` for missing files and `RuntimeException` for +empty / unreadable files. + +--- + +## TimersLibrary + +```php +$library->getTimers(); // Timer[] +$library->count(); // int +$library->getTimerByUuid($uuid); // ?Timer (case-insensitive) +$library->getTimerByName($name); // ?Timer +$library->addTimer($name, $uuid); // Timer +$library->removeTimer($uuid); // bool +$library->getClockFormat(); // string +$library->setClockFormat('HH:mm'); // void +$library->getApplicationInfo(); // ?\Rv\Data\ApplicationInfo +$library->getDocument(); // \Rv\Data\TimersDocument +``` + +--- + +## Timer + +```php +$timer->getUuid(); +$timer->setUuid($uuid); +$timer->getName(); +$timer->setName($name); +$timer->getConfiguration(); // ?\Rv\Data\Timer\Configuration +$timer->isCountdown(); +$timer->isCountdownToTime(); +$timer->isElapsedTime(); +$timer->getDurationSeconds(); // ?int for countdown timers +$timer->getProto(); // \Rv\Data\Timer +``` + +--- + +## CLI Tool + +```bash +php bin/parse-timers.php /path/to/Timers +``` + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `src/TimersLibrary.php` | Document wrapper with UUID / name indexes | +| `src/Timer.php` | Single timer wrapper | +| `src/TimersFileReader.php` | Reads the `Timers` file | +| `src/TimersFileWriter.php` | Writes the `Timers` file | +| `bin/parse-timers.php` | CLI summary tool | +| `generated/Rv/Data/TimersDocument.php` | Generated document class | + +--- + +## Scope Notes + +Timer configuration is exposed as the generated protobuf sub-message. Helper +methods cover the oneof timer type and countdown duration without hiding raw +access for callers that need advanced fields. diff --git a/doc/api/workspace.md b/doc/api/workspace.md new file mode 100644 index 0000000..bac30de --- /dev/null +++ b/doc/api/workspace.md @@ -0,0 +1,83 @@ +# Workspace Library API + +> PHP module for reading, modifying, and writing the ProPresenter `Workspace` file. + +## Quick Reference + +```php +use ProPresenter\Parser\WorkspaceFileReader; +use ProPresenter\Parser\WorkspaceFileWriter; + +$library = WorkspaceFileReader::read('/path/to/Workspace'); + +foreach ($library->getScreens() as $screen) { + $screen->getName(); + $screen->getUuid(); + $screen->getScreenType(); +} + +WorkspaceFileWriter::write($library, '/path/to/Workspace'); +``` + +--- + +## File Layout + +The `Workspace` file is `Rv\Data\ProPresenterWorkspace` from `proworkspace.proto`. +Its `pro_screens` entries are `Rv\Data\ProPresenterScreen` messages from +`proscreen.proto`. + +--- + +## WorkspaceLibrary + +```php +$library->getDocument(); +$library->getScreens(); +$library->getScreenByName('StageDisplay'); +$library->getScreenByUuid('C86D...'); // case-insensitive +$library->addScreen($screen); +$library->removeScreen($uuid); +$library->count(); +$library->getAudienceLooks(); +$library->getMasks(); +$library->getVideoInputs(); +$library->getSelectedLibraryName(); +$library->setSelectedLibraryName('Library'); +``` + +--- + +## Screen + +```php +$screen->getName(); +$screen->setName('New name'); +$screen->getUuid(); +$screen->setUuid('...'); +$screen->getScreenType(); +$screen->getIndex(); +$screen->getProto(); +``` + +Use `getProto()` for detailed arrangement, background, and screen geometry data. + +--- + +## CLI Tool + +```bash +php bin/parse-workspace.php /path/to/Workspace +``` + +--- + +## Key Files + +| File | Purpose | +|------|---------| +| `src/WorkspaceFileReader.php` | Reads the `Workspace` file | +| `src/WorkspaceFileWriter.php` | Writes the `Workspace` file | +| `src/WorkspaceLibrary.php` | Document wrapper and indexes | +| `src/Screen.php` | Single screen wrapper | +| `bin/parse-workspace.php` | CLI summary tool | diff --git a/doc/keywords.md b/doc/keywords.md index 1db1e96..230842f 100644 --- a/doc/keywords.md +++ b/doc/keywords.md @@ -13,30 +13,34 @@ | ZIP | [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md), [formats/pp_playlist_spec.md](formats/pp_playlist_spec.md) | | ZIP64 | [formats/pp_playlist_spec.md](formats/pp_playlist_spec.md), [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md) | | binary format | [formats/pp_song_spec.md](formats/pp_song_spec.md), [formats/pp_playlist_spec.md](formats/pp_playlist_spec.md), [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md) | +| JSON | [api/communication-devices.md](api/communication-devices.md) | ## Song Structure | Keyword | Document | |---------|----------| | song | [api/song.md](api/song.md) | -| group | [api/song.md](api/song.md), [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 4 | +| group | [api/song.md](api/song.md), [api/groups.md](api/groups.md), [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 4 | | slide | [api/song.md](api/song.md), [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 5 | | arrangement | [api/song.md](api/song.md), [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 6 | | translation | [api/song.md](api/song.md), [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 7 | | verse | [api/song.md](api/song.md) | | chorus | [api/song.md](api/song.md) | | lyrics | [api/song.md](api/song.md) | -| CCLI | [api/song.md](api/song.md), [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 3 | +| CCLI | [api/ccli.md](api/ccli.md), [api/song.md](api/song.md), [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 3 | ## Bundle Structure | Keyword | Document | |---------|----------| -| bundle | [api/bundle.md](api/bundle.md), [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md) | +| bundle | [api/bundle.md](api/bundle.md), [api/theme.md](api/theme.md), [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md) | | probundle | [api/bundle.md](api/bundle.md), [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md) | | pro6x | [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md) Section 1 | | LocalRelativePath | [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md) Section 3 | | absolute path | [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md) Section 2 | +| theme | [api/theme.md](api/theme.md) | +| theme folder | [api/theme.md](api/theme.md) | +| assets | [api/theme.md](api/theme.md), [api/bundle.md](api/bundle.md) | ## Playlist Structure @@ -70,25 +74,86 @@ | Macros file | [api/macros.md](api/macros.md) | | MacroCollection | [api/macros.md](api/macros.md) | | MacroLibrary | [api/macros.md](api/macros.md) | -| media | [api/song.md](api/song.md), [api/bundle.md](api/bundle.md), [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 5, [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md) Section 3 | -| image | [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 5, [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md) Section 3 | +| media | [api/song.md](api/song.md), [api/bundle.md](api/bundle.md), [api/theme.md](api/theme.md) | +| image | [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 5, [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md) Section 3, [api/theme.md](api/theme.md) | | video | [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 5, [formats/pp_bundle_spec.md](formats/pp_bundle_spec.md) Section 6 | -| cue | [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 5 | +| cue | [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 5, [api/props.md](api/props.md) | | label | [api/labels.md](api/labels.md), [api/song.md](api/song.md), [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 5 | | Labels file | [api/labels.md](api/labels.md) | | LabelLibrary | [api/labels.md](api/labels.md) | | LabelsFileReader | [api/labels.md](api/labels.md) | -| color | [api/labels.md](api/labels.md), [api/macros.md](api/macros.md) | +| color | [api/labels.md](api/labels.md), [api/macros.md](api/macros.md), [api/groups.md](api/groups.md), [api/clear-groups.md](api/clear-groups.md) | + +## Global library files + +| Keyword | Document | +|---------|----------| +| Groups file | [api/groups.md](api/groups.md) | +| GroupLibrary | [api/groups.md](api/groups.md) | +| GroupDefinition | [api/groups.md](api/groups.md) | +| ClearGroups file | [api/clear-groups.md](api/clear-groups.md) | +| ClearGroup | [api/clear-groups.md](api/clear-groups.md) | +| ClearGroupDefinition | [api/clear-groups.md](api/clear-groups.md) | +| CCLI file | [api/ccli.md](api/ccli.md) | +| CCLILibrary | [api/ccli.md](api/ccli.md) | +| copyright | [api/ccli.md](api/ccli.md) | +| license | [api/ccli.md](api/ccli.md) | +| Messages file | [api/messages.md](api/messages.md) | +| MessageLibrary | [api/messages.md](api/messages.md) | +| Message | [api/messages.md](api/messages.md) | +| token | [api/messages.md](api/messages.md) | +| Timers file | [api/timers.md](api/timers.md) | +| TimersLibrary | [api/timers.md](api/timers.md) | +| Timer | [api/timers.md](api/timers.md) | +| Clock | [api/timers.md](api/timers.md) | +| countdown | [api/timers.md](api/timers.md) | +| Stage file | [api/stage.md](api/stage.md) | +| StageLibrary | [api/stage.md](api/stage.md) | +| StageLayout | [api/stage.md](api/stage.md) | +| stage display | [api/stage.md](api/stage.md) | +| Workspace file | [api/workspace.md](api/workspace.md) | +| WorkspaceLibrary | [api/workspace.md](api/workspace.md) | +| Screen | [api/workspace.md](api/workspace.md) | +| audience look | [api/workspace.md](api/workspace.md) | +| mask | [api/workspace.md](api/workspace.md) | +| video input | [api/workspace.md](api/workspace.md) | +| Props file | [api/props.md](api/props.md) | +| PropLibrary | [api/props.md](api/props.md) | +| Prop | [api/props.md](api/props.md) | +| TestPatterns file | [api/test-patterns.md](api/test-patterns.md) | +| TestPatternsLibrary | [api/test-patterns.md](api/test-patterns.md) | +| test pattern | [api/test-patterns.md](api/test-patterns.md) | +| Calendar file | [api/calendar.md](api/calendar.md) | +| CalendarLibrary | [api/calendar.md](api/calendar.md) | +| CalendarEvent | [api/calendar.md](api/calendar.md) | +| schedule | [api/calendar.md](api/calendar.md) | +| KeyMappings file | [api/key-mappings.md](api/key-mappings.md) | +| KeyMappingsLibrary | [api/key-mappings.md](api/key-mappings.md) | +| KeyMapping | [api/key-mappings.md](api/key-mappings.md) | +| hot key | [api/key-mappings.md](api/key-mappings.md), [api/groups.md](api/groups.md) | +| CommunicationDevices file | [api/communication-devices.md](api/communication-devices.md) | +| CommunicationDevicesLibrary | [api/communication-devices.md](api/communication-devices.md) | +| CommunicationDevice | [api/communication-devices.md](api/communication-devices.md) | +| MIDI | [api/communication-devices.md](api/communication-devices.md) | +| OSC | [api/communication-devices.md](api/communication-devices.md) | +| Theme | [api/theme.md](api/theme.md) | +| ThemeBundle | [api/theme.md](api/theme.md) | +| ThemeSlide | [api/theme.md](api/theme.md) | +| ThemeAsset | [api/theme.md](api/theme.md) | ## PHP API | Keyword | Document | |---------|----------| -| read | [api/song.md](api/song.md), [api/playlist.md](api/playlist.md), [api/bundle.md](api/bundle.md), [api/macros.md](api/macros.md), [api/labels.md](api/labels.md) | -| write | [api/song.md](api/song.md), [api/playlist.md](api/playlist.md), [api/bundle.md](api/bundle.md) | +| read | [api/song.md](api/song.md), [api/playlist.md](api/playlist.md), [api/bundle.md](api/bundle.md), [api/macros.md](api/macros.md), [api/labels.md](api/labels.md), [api/groups.md](api/groups.md) | +| write | [api/song.md](api/song.md), [api/playlist.md](api/playlist.md), [api/bundle.md](api/bundle.md), [api/macros.md](api/macros.md), [api/labels.md](api/labels.md), [api/groups.md](api/groups.md), [api/theme.md](api/theme.md) | | generate | [api/song.md](api/song.md), [api/playlist.md](api/playlist.md) | -| parse | [api/song.md](api/song.md), [api/playlist.md](api/playlist.md), [api/bundle.md](api/bundle.md), [api/macros.md](api/macros.md), [api/labels.md](api/labels.md) | +| parse | [api/song.md](api/song.md), [api/playlist.md](api/playlist.md), [api/bundle.md](api/bundle.md), [api/macros.md](api/macros.md), [api/labels.md](api/labels.md), [api/groups.md](api/groups.md) | | MacrosFileReader | [api/macros.md](api/macros.md) | +| MacrosFileWriter | [api/macros.md](api/macros.md) | +| LabelsFileWriter | [api/labels.md](api/labels.md) | +| GroupsFileReader | [api/groups.md](api/groups.md) | +| GroupsFileWriter | [api/groups.md](api/groups.md) | | ProFileReader | [api/song.md](api/song.md) | | ProFileWriter | [api/song.md](api/song.md) | | ProFileGenerator | [api/song.md](api/song.md) | @@ -98,9 +163,11 @@ | ProBundleReader | [api/bundle.md](api/bundle.md) | | ProBundleWriter | [api/bundle.md](api/bundle.md) | | PresentationBundle | [api/bundle.md](api/bundle.md) | +| ThemeFileReader | [api/theme.md](api/theme.md) | +| ThemeFileWriter | [api/theme.md](api/theme.md) | | Song | [api/song.md](api/song.md) | | PlaylistArchive | [api/playlist.md](api/playlist.md) | -| CLI | [api/song.md](api/song.md), [api/playlist.md](api/playlist.md), [api/macros.md](api/macros.md), [api/labels.md](api/labels.md) | +| CLI | [api/song.md](api/song.md), [api/playlist.md](api/playlist.md), [api/macros.md](api/macros.md), [api/labels.md](api/labels.md), [api/groups.md](api/groups.md) | | command line | [api/song.md](api/song.md), [api/playlist.md](api/playlist.md), [api/macros.md](api/macros.md), [api/labels.md](api/labels.md) | ## Protobuf @@ -109,13 +176,25 @@ |---------|----------| | Presentation | [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 3 | | CueGroup | [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 4 | -| Cue | [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 5 | +| Cue | [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 5, [api/props.md](api/props.md) | | Action | [formats/pp_song_spec.md](formats/pp_song_spec.md) Section 5 | | Playlist | [formats/pp_playlist_spec.md](formats/pp_playlist_spec.md) Section 3 | | PlaylistItem | [formats/pp_playlist_spec.md](formats/pp_playlist_spec.md) Section 5 | | UUID | [formats/pp_song_spec.md](formats/pp_song_spec.md), [formats/pp_playlist_spec.md](formats/pp_playlist_spec.md) | | field number | [formats/pp_song_spec.md](formats/pp_song_spec.md) Appendix | | proto | [formats/pp_song_spec.md](formats/pp_song_spec.md) | +| Template.Document | [api/theme.md](api/theme.md) | +| ProPresenterWorkspace | [api/workspace.md](api/workspace.md) | +| ProGroupsDocument | [api/groups.md](api/groups.md) | +| ClearGroupsDocument | [api/clear-groups.md](api/clear-groups.md) | +| MessageDocument | [api/messages.md](api/messages.md) | +| TimersDocument | [api/timers.md](api/timers.md) | +| Stage.Document | [api/stage.md](api/stage.md) | +| PropDocument | [api/props.md](api/props.md) | +| TestPatternDocument | [api/test-patterns.md](api/test-patterns.md) | +| CalendarDocument | [api/calendar.md](api/calendar.md) | +| KeyMappingsDocument | [api/key-mappings.md](api/key-mappings.md) | +| CCLIDocument | [api/ccli.md](api/ccli.md) | ## Troubleshooting diff --git a/doc/reference_samples/CCLI b/doc/reference_samples/CCLI new file mode 100644 index 0000000000000000000000000000000000000000..3331b012eb7bc6313cee37c04741b49ed77cba87 GIT binary patch literal 1292 zcmaKsPmj|^7{T8Od**C!0)Z zMP!4-ta=#ArTa&(-`M`7=~ccj|M_6&&Uc!q$WyHT4`*hj zmHlw;ZC=pbCx(M!c%R?4yILM^23~b{wzB?a4QN|)cRqUZ**&)nEMH3CBcF~pE_y2^ky1piDojSgWF$;Rxyd1*|0osxS)V-mqF3OlDXvWs7dY|@ z1f;-Gzd#TbI7+5?xj-*15DdMGz{$G{ycKX^jQ87l2M(!~oezJW0e4gHojmo_&0jMp zd$nAneg5~^-{1ZEk!wq}>u+pf%DNin4G6O#81yvV)NzmmiO*B|F-wE_jPb854g4sK zdnjKSL_X^#bOUYSZW1%rj&K*@ZXEI$^nIEVhLo3^zix14XTh>X&~+mVHZQ_9>-9`hcQT1#WD*l)624nVEG?5*$GAk)4QcGNA@w6MV_~n) zNZj=UmQVy?*0j{L6dZJQ1z#dG<~`QL1H~OX5A2G8AWnuXLO5kKxkRpn9oAP3q>Sf5 zT{$1Jh+w$H9a4tjnPEE$(>|dXHj}|sDr}D{1u7Sa&r_$ufW#?LJ|1qg3B8798sMa~ zzE@HH8r@cu_WsGiSbe_j9*qCn&JP9!$}W9|f|a@PVS4NykmjG=tn6N1z3YxYHGOjG rfAezHcI2kJin;!@ccA$P?3mg7kSeO8D%DEYdfPB z1qN!iD{8H_YEjU}$in`F_G(F6h0b%|xv!WQ7v9C?zMuO&-}61^4CC;H;U3H`DeVQA z>@=V=3Y>`%5S!Mar@h~)s^Q5gVQU|ZCiL`SSo}ktD+SV zGCk?lWWnadz!($>wLlSBv!g;LyZQfcLdqAgRS)-QxTjH24d7&_>!=AoN**^ZvTC<)^%+>t8075Q9{I! zoq-^OBy(~Caw|hdjXX-vB=&lcIDcHfK5nmEmc5?&j#CPg*-$kp81g4(29|3Mn3LD& zePTM!^eEzdarW%E{pj?d=j`Y>233jy6)2IYS;0B*LQ5!8i=tF^BB>ci6X*LYIBvhZ zaG|ejVYsjMOE976_X)g}32KiZc+}H=Wh|0WWiyUheroA#d2>9v{U?b!a&^}HL5GTk zF(IV@h3Kgurobv$AaQQAmx-fvGn6INPw)Tw(rESO%j5R$kM~e3RaKEH2pI#I;;8$~ z6!098$i>T`y_Px5P;PnH{{F)~z-tv?N_HMu0-J2Lhce_e)rO9_#JF%F+Gb%ZhK$5e zl*gm`sG;66X)w%{6^!#vGY&-rvdCL%naKjA)P+oo=!IF(S zM~8bWCZZt+ysn+6BwFNDlZ+4zqYxQyMPbcChpD6SP!Q+5Fdqsq#aXRQ0>KRAOfntU z$~w`5Xf~GWShP299Pbr9-6;w}X%TD!?UMm4ynrp3!7yuFra?>I_aGV$Qgm%hr9In& zmb)@pW?NJbs!l(!g`VgXm12cf+JJJL zBE$uQP-sX>G*);KXn#=4wiH#TYq#!OLrcSHu0sxm#Z2qfQUEa0diFUea+bwDr*~wZZT}4Wvesy;iguElnxPS{)YwyoyRk&XuCkA0FD6z*e@WQ?fGp z=IcG<_R~)u>sQjQZvN3FPWMBB)QP(1XkI-g0py{M^r5!o=J9*H-PN(FwE3U^>4Pxc zN(r-9g@RyYp=46qA((*VS7exV5W=TsZr}O`8z-p78m7^A-@drge*W36KAVf3s^C(> zNI4+msE4J-DftY7Cm!f1(LG|#+`&^F>h7I8zgOnCdWx#)ij#s{rtIeeVMiby{0oKMcuoM z?re>$L+XXI3X`U7s%f*#g;jrG|MJ|dcl+y`Norl9_n?y|uW299Ve$r;;2}k;Ok$;3 zj0X?Bb$H+2S6_dqjOpPoN?V>X2bxp5ZKCLU+amBtM?caoFiJJ^dHu-R`q2kZ(%@f9 CZu8&( literal 0 HcmV?d00001 diff --git a/doc/reference_samples/KeyMappings b/doc/reference_samples/KeyMappings new file mode 100644 index 0000000..f427670 --- /dev/null +++ b/doc/reference_samples/KeyMappings @@ -0,0 +1,2 @@ + + "" 117899279 \ No newline at end of file diff --git a/doc/reference_samples/Messages b/doc/reference_samples/Messages new file mode 100644 index 0000000000000000000000000000000000000000..875ac856c555b3c3b7fe476012779d9555edf67b GIT binary patch literal 462 zcma)&PfNov7{=|8u~jxl^f2b7jGe# zC@foGeEnSJ%XJ-0SIcFcy#xe57FF<2F7hhyLWP>sy2`V~z_}7) z4b_T)Hd+I<+=2{644Q<@@-&U8;KR;VdiS1Jui3}Aaoe~*8+~f3thP7>thZ7QLPP{i zs)S^iQZ3BaQ?cp3%j@9EKmON#Ozsp%7vAYy)Yy#La6-7*>VaR5a zos*9I1JrKsZSMQ$8twPIe&gbB3pp#-ZZ8*%#KtccmT8en;EoF1#uAj27!o4lNE;R# SE>L%pzvaOsn-^6%FX}&_>S}EO literal 0 HcmV?d00001 diff --git a/doc/reference_samples/Props b/doc/reference_samples/Props new file mode 100644 index 0000000000000000000000000000000000000000..3d0913ccf79dc70e0519101b63dd051c445b9301 GIT binary patch literal 17779 zcmeI4VT>flS%!CfwwZ2X=R8~-XVAstwG*2|#Z^^TRaeD!cB{Ipb-K$rr!!F?nje|n z>D?V?Zf2R8CFfXCE+|qE5Mo)eVq4gt7!t=O1PNrB1QCNtlpi33KnQ^X79a%5pF{ir zBxLh;_ulU9oqZX&z3fIz>u$EUySl5pXX@Ga``)i6JY26eZmi#Y@W$I}x7Fs~T5lY@ zZa#=8XPlZ?HJ%K^cZYYe%<)LYf`~{kBDjo+jbcT7?Bj@OuZ7PV^;4ta`nYu`b91L( z4T{4uCz*{5Ct5j4gi(qZsvHrFMlv-zic`6LSo19xt#;Rh=`yO&Kac<;oegnQOAuk&3nQ|Fjp@6{#&Y*6Zbb7!E3J<%hQXu zlq-M#=;Du6JI^n;$7;oc2yFt4itB7Z6*@QImt4kh)4t{HscwISi-q39;S*LXT$fe z^!oXGjvhVwfpI<>A6?2jXY$d}bhx~6CLc`3NAV7w8s%fGWAn+p+v^;)<8h8X4`R#U zU7Qm9XPhw|{luAdfltJp^{{dK$S57<_~W+dhcS<2+R3zv}&wc zeRbi;$HG0k>sIab?VE36eX)7yCfvfp+lrUN#ml>jmk<4T)!?_s7q0%$;vLnsA6h(e zL)m8YUjAiaDY)gSCyV9F3lDwsx@J4PC%oxc-v8Nr(p&C4kZI*OWs}{E)P4{ z#bd?7>bFULb~0Y>^m~I8Xe(B@n8>cVZcJAU)|a#`4X^!$xJsu*n?X!1N0Bul9$!bgTUktD=e z#hlGdY!mjjVj49VXP!+q5z~=TjHGZB(+>bUQop%*|v!#$47(aXM*tc zdiZyERVVbX;F?2dy|jOA638Wk7p*JRYqswU-$! zmP>!$yR{#?ihnp!yPK~}I!jDx8n)Lvqi*@KwXOWEHyU&%8>3FY{WHtOhDmRAZITaG zI?I#ch=yVL5XR6(t@iQ?Z?%UTlRj;aC!^s<@{jhqlQk%k*5>caQ7c)yWZ1W_qUW*R z>~$L)({r$OU?&NG>et_L-*=w+>h~fc#8j$05F`r-iZo4$%V-8HCDNwUMcQQGYV}dQ zYY|ZVY6(T^x#rd~5;Kt!;j92bog;>;M5Q*-BBPDlkLQCyZzUhK9$a1<a;X8VpoLXw~u}J^N*jKL9u$5uPj`?wXdQ0$?Nw3ifss| zHv{MU3clXBbDYk5Zy2|Uiz%xx`E<~HDhPim2)|ViUnntYDWdr_ z;{<x?VHjMJG+DbKjMA|?ww%(UZl@!gC28k7Gv(~hOK+*9pX4E4!co|nV@ zYACpcz>~enYQL9vTitxzYTvyqDt-(QgkEzjCNxF@G08|0StPI&5MxqHiLessJH*eH z@ts(Fro>{Rql7ANiD8sO_#v!yDr3aB$Vi5WJ4tNg;E5rGFG=)2z3IY zIJG8W$qb1*W7_|7%IlB!H4{USl)PCotQXt)N;hRI|2EhCEwyCrIC{y~hoR3O2eyu!~u}Da*GQyJ>l8?)b7_AIRP3)L%*&bU9p?V@UT-O7eZPB%c9b6Oq7~jv^c&Jts!MqmCH! z(0a-{xoNUI9yDP;e<}$7pdLP3CD3Gj6>vAHcio zP7o@e=;v_gM;q&~D6xw2j}ni6R^m~`G(o7Hl0?AH76!RMSkh3WMrH7lX~H1!$^xBV z4Vv&;emMxgT@Sxf;*rM&6jiW(aM7^Cpe&t>GonlsQDFpUZWc7}R-MR8`x=k`KEvbQ zYZS`ccn%nc?a;;6k5%K1#8ZtY@A z`dkT0uHqOTG%}Fd!@!ONl6eZ0wM@AvgU7+rta0=4&Y(Bwv`)VA`~ChV62DQ!*@^>Q zD1nTN(7cMzE$VAB7&Iupp&xkB@? z#DXl`px6eAk9vk|-X*!hpRW)oQ|1vD6VH$#6cGYum>`y<&}(tbDQXQx<40WHnZR`d z;!#7qymkgzgo-THsobdspDj_C3hPB`EvB#(0LnCq0y9W53cExKtXh#a zZaWQM`QgG)T15%^rT+3?mplYHRc1A+b#w|3B>{%DhowSI7E9DyGj4pv!{>tLZv^4z zgYeJm;fs|n1P6^MB5IDNoKo?J`<*S`8D6`IAUltIj_P(?%Gc{mn(5=Gz)=7jh}TvEpUg_4qj zF)JlpU8fV!6`lyO3FebnMm?YxArI&pZ$H(+v7O_NEBsS`t+^nTLdB4n~kb z#Wk^t;>2m@QO?5?gWBxUc6mN%ekll_48p%C(~PK9%uJ@KHYxIxMNYstAl5O3OW{Uj z)?-fNY?jMz*)Ff_Yc2WOOiO;x$$@)1%jIGg`~ZSdNf;?A=s{VK%wAlF+V2rF>1h3iVeS-0iG%?d}iZSDw=`aSn0p=BEGF) z{w|L%Kd_o>VpGUQl#j#ZhDS|U>U|6>iqt6{q&?VDm@q${FMEknAQ6R=#3jXS7=sQR zX5}DXSsY=ILRD$w;DZyy_AtfAoik_paI-KW8do^YH(t#D?)7C;kqHl>j1qN^TR;Mq z=;CHl1n@FZmI}!ljR){m44C6MSn8vu0Nvz+s-tP9k5?_HnNwM{oMtXj)pDBh`l{tL qb*7u=oT4nNYB^2qLe+8_^KP@{6glpy_s; literal 0 HcmV?d00001 diff --git a/doc/reference_samples/Stage b/doc/reference_samples/Stage new file mode 100644 index 0000000000000000000000000000000000000000..c9a3d1c0f311ab8c9fa58e7f51ebdeec9da0063d GIT binary patch literal 57284 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 literal 0 HcmV?d00001 diff --git a/doc/reference_samples/TestPatterns b/doc/reference_samples/TestPatterns new file mode 100644 index 0000000..ffd3001 --- /dev/null +++ b/doc/reference_samples/TestPatterns @@ -0,0 +1,3 @@ + +*"& +$BCDE1115-AD40-4BA4-A33A-BFFE3E87223B \ No newline at end of file diff --git a/doc/reference_samples/Timers b/doc/reference_samples/Timers new file mode 100644 index 0000000000000000000000000000000000000000..93316f7aed106d92794b8d7735781e37b13a8a7e GIT binary patch literal 420 zcmYk&O-jQ+6u@y)u(k=+I*OEl5Q>N{24*HRNv5k#=EI_hQdceu4O*}Xq*J|&JHZQh zfnLQEh&M1P75Y|h_2d7Zd)jh9v$fxC_MD#6KWKq&qu&k@!36O*0(@_R5vo_$)*G~oh0d@yoY5)KL literal 0 HcmV?d00001 diff --git a/doc/reference_samples/Workspace b/doc/reference_samples/Workspace new file mode 100644 index 0000000000000000000000000000000000000000..df60a03e85c43c808131554af88ed04f7b561970 GIT binary patch literal 12739 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 BTPgqm literal 0 HcmV?d00001 diff --git a/doc/reference_samples/pp-config/CCLI b/doc/reference_samples/pp-config/CCLI new file mode 100644 index 0000000000000000000000000000000000000000..3331b012eb7bc6313cee37c04741b49ed77cba87 GIT binary patch literal 1292 zcmaKsPmj|^7{T8Od**C!0)Z zMP!4-ta=#ArTa&(-`M`7=~ccj|M_6&&Uc!q$WyHT4`*hj zmHlw;ZC=pbCx(M!c%R?4yILM^23~b{wzB?a4QN|)cRqUZ**&)nEMH3CBcF~pE_y2^ky1piDojSgWF$;Rxyd1*|0osxS)V-mqF3OlDXvWs7dY|@ z1f;-Gzd#TbI7+5?xj-*15DdMGz{$G{ycKX^jQ87l2M(!~oezJW0e4gHojmo_&0jMp zd$nAneg5~^-{1ZEk!wq}>u+pf%DNin4G6O#81yvV)NzmmiO*B|F-wE_jPb854g4sK zdnjKSL_X^#bOUYSZW1%rj&K*@ZXEI$^nIEVhLo3^zix14XTh>X&~+mVHZQ_9>-9`hcQT1#WD*l)624nVEG?5*$GAk)4QcGNA@w6MV_~n) zNZj=UmQVy?*0j{L6dZJQ1z#dG<~`QL1H~OX5A2G8AWnuXLO5kKxkRpn9oAP3q>Sf5 zT{$1Jh+w$H9a4tjnPEE$(>|dXHj}|sDr}D{1u7Sa&r_$ufW#?LJ|1qg3B8798sMa~ zzE@HH8r@cu_WsGiSbe_j9*qCn&JP9!$}W9|f|a@PVS4NykmjG=tn6N1z3YxYHGOjG rfAezHcI2kJin;!@ccA$P?3mg7kSeO8D%DEYdfPB z1qN!iD{8H_YEjU}$in`F_G(F6h0b%|xv!WQ7v9C?zMuO&-}61^4CC;H;U3H`DeVQA z>@=V=3Y>`%5S!Mar@h~)s^Q5gVQU|ZCiL`SSo}ktD+SV zGCk?lWWnadz!($>wLlSBv!g;LyZQfcLdqAgRS)-QxTjH24d7&_>!=AoN**^ZvTC<)^%+>t8075Q9{I! zoq-^OBy(~Caw|hdjXX-vB=&lcIDcHfK5nmEmc5?&j#CPg*-$kp81g4(29|3Mn3LD& zePTM!^eEzdarW%E{pj?d=j`Y>233jy6)2IYS;0B*LQ5!8i=tF^BB>ci6X*LYIBvhZ zaG|ejVYsjMOE976_X)g}32KiZc+}H=Wh|0WWiyUheroA#d2>9v{U?b!a&^}HL5GTk zF(IV@h3Kgurobv$AaQQAmx-fvGn6INPw)Tw(rESO%j5R$kM~e3RaKEH2pI#I;;8$~ z6!098$i>T`y_Px5P;PnH{{F)~z-tv?N_HMu0-J2Lhce_e)rO9_#JF%F+Gb%ZhK$5e zl*gm`sG;66X)w%{6^!#vGY&-rvdCL%naKjA)P+oo=!IF(S zM~8bWCZZt+ysn+6BwFNDlZ+4zqYxQyMPbcChpD6SP!Q+5Fdqsq#aXRQ0>KRAOfntU z$~w`5Xf~GWShP299Pbr9-6;w}X%TD!?UMm4ynrp3!7yuFra?>I_aGV$Qgm%hr9In& zmb)@pW?NJbs!l(!g`VgXm12cf+JJJL zBE$uQP-sX>G*);KXn#=4wiH#TYq#!OLrcSHu0sxm#Z2qfQUEa0diFUea+bwDr*~wZZT}4Wvesy;iguElnxPS{)YwyoyRk&XuCkA0FD6z*e@WQ?fGp z=IcG<_R~)u>sQjQZvN3FPWMBB)QP(1XkI-g0py{M^r5!o=J9*H-PN(FwE3U^>4Pxc zN(r-9g@RyYp=46qA((*VS7exV5W=TsZr}O`8z-p78m7^A-@drge*W36KAVf3s^C(> zNI4+msE4J-DftY7Cm!f1(LG|#+`&^F>h7I8zgOnCdWx#)ij#s{rtIeeVMiby{0oKMcuoM z?re>$L+XXI3X`U7s%f*#g;jrG|MJ|dcl+y`Norl9_n?y|uW299Ve$r;;2}k;Ok$;3 zj0X?Bb$H+2S6_dqjOpPoN?V>X2bxp5ZKCLU+amBtM?caoFiJJ^dHu-R`q2kZ(%@f9 CZu8&( literal 0 HcmV?d00001 diff --git a/doc/reference_samples/pp-config/KeyMappings b/doc/reference_samples/pp-config/KeyMappings new file mode 100644 index 0000000..f427670 --- /dev/null +++ b/doc/reference_samples/pp-config/KeyMappings @@ -0,0 +1,2 @@ + + "" 117899279 \ No newline at end of file diff --git a/doc/reference_samples/pp-config/Labels b/doc/reference_samples/pp-config/Labels new file mode 100644 index 0000000000000000000000000000000000000000..c3803b9cfea9131fa60cc0825b65e8a296c949eb GIT binary patch literal 527 zcmd<$72@_uO)W}QaLdohOy%Me;_=KYE-5O_P0cGw%;5q`hi9gyq!wl5=cMLw2@46j zrxq6!%1?T+I zypojs@;oUK-m__wL@QL&Tl>E^aqWngHq=Q0;k!Eb<9W?p(usuVx(+c$6QWI;wi zG#Uy?;?#&@7sx`07G)s;gcgV!rT9fJUbtW0h%{) zf`FYUNR{l&88e{f=?jTtC{-{wP*7C>YM3=s(q42zKfk@~&mTW*Ar6oe67>UyOkQzn zW?m97Oo3sm;GLP1BgG1l*ANmwm&aj}ln{Gx6)+kU3^79nE@uRk+q`L$oh-;L5F6lf O#$bIppb$L+@eTk1IH$e< literal 0 HcmV?d00001 diff --git a/doc/reference_samples/pp-config/Macros b/doc/reference_samples/pp-config/Macros new file mode 100644 index 0000000000000000000000000000000000000000..f49e40e764d72048efd2ec9749d7f637da9e2d7b GIT binary patch literal 9529 zcmd^FYlvOfb)GwRWcwyg>=RF`%ANKyOw(@3p?Q*0;Vj{l(jR&7RxeeAk}6y}iAqx7^;`HNSL+pqz2)jBft6 z*T1j-w&JO#JWG<3=R`_nNC=t}ZM|oPS;?7cnj`C5Tf@z@(QvZ4H8_37pE|Seo;&~g zwhyl?k#^M!Ec1CdEP;;5%YX77_wimNDS?_Ib2 zP45Jb^p5rzmU_W$Bs$h(~xiMTDo!c73_3g>l+WOOzQ+M|7 zxMQyV8T|3#L9h9CdnZmKOC@DOOVC8RtVnP)lB_kgNpb@hG;caK8m#3$~EpuVVth_R@fUpc;xBdl(X)+ft;a^b~qPT;NW$+_hN2M%0v(~p1u zr9%t%KL7m6;zyrfoW|`F-AzMOz=I${IEKK7 zgNO3b6NA%R8~A^B-_#HFz|=XzqDsUmjr(%NfPqMf7bS>5g-wa=ZffZ>?RjE|MfgMs z4|rc{%Snhr60JkZ!Dl16$5(&;Tk{|2pRZrNj>yyco=udO2<%%?EJ3hwXF!Hn7CCo@ zBMo%3$DbXqZa?1qcN>wyqP04ELRB#pQvx|n@*mmqeEvjGFruIB$XZ>Wli z;uwUxT#$<~z-O8fWsR0vn&gVx$(vhlX5K7y;ippI+#0pOW=Et~fV`ECSYI=cCfv*J z9cXrx3T`>=Z1r@y8YPsv!lt{@aMj=?YtpW8zyyT)JA&6tuJhEJo}orwniwKvLH+_9MQq6flEtt z?MYG>9rz_jtmX#$FN}wy$?muI#l@I$#(ncV!^j{RMtK6s%0&@}j``|qhIXF}S{5G5*sB56t{o<~7=qTaIF z-QDb#r{2)GGWrA~BGyiZ(nP$mi6o{n*W4z@?G@f=Jv^H?xDTlAfGK6NvIpi#C=7jE zo0t;!K6`r68@q(=s@||ciC_!ZNUiNsD6}JK(e0uH&SX|O+nFsl-{~?|ImVKuM01`| zGZQj`*Y?5T6VxtBrEM12L55yAZJPH@`z0`O;JLw}g?ql8Ru=bu>Ygd-_)rTuxH1ZF z2~q=)pvW<=xj+%5O6L*`_qln?s|zxz9XJ49Byqc zKa}71{zs3Ve#9R;@PQ}K?d|QGyC1aom+o&mUOo5x;k#Rc{ZG#wK6q1OtQHz<{na7DBv6b3{{~${!n@iGYKWMRT!@?)FaKXc^io$XyyTqP zfNH@&o;}m_2E2bh_7aX@1R#|XcY)%or6-2I+~2}=W4@w z7l<32@Rx?wE1)DOD-bD+*3~PRbsPiRG!#rTM4n7w_}s!D^mKi^m=xeFP-ct_$T{KB zLqR~4=U_8hg8$~GfARXX*ZfNxa;uojlqNzg0xea+T<-B51O!;b@EHTNksYpC*%dtZ znZNw=m4(0i$KO1(Sbuaqt{Yek#sLmifDAwZfJy)afe?dlnt<`lYe4Mvd3NW=>A-T6 zsC#$aWQxQH!3n08N>)7z2*reLOv2c13kxP*yl$n|uX$Y^nGg)h7Bm|~2`P#wK|He} z6v47HP)ROccNeDq6PeF`x1$_j$QD(XXev;jI7$<`C2|X0jXPynN};)TC%TWWkH=?6 z zIN3kZ7KDuH(ZH-u7*nN>=&c%URhYn|a>{b&W;5ITxaACiXIgZXjT9zwD43w*P&v_+ zLd49HytB5s?c~WprACPN_kaAqD+{0g#@ALBE1B!y{q@$8Wl3Q+0K6drVKPl9&m2K# zVA9?(ld3=WgcrYgep2PW-uHU{IlpwU-=-)kc+yNl0h1Ek5@;h(R52@yjPh4XPEZKm zbb4#`+%SzcpB%4#u$f!>t-h!(smv2;iS!130b9;M9a$X^QT8-)TSD%;AwteGQ6(TF7yBUxI``GO&@G#WTal&QC@cPL5Z%o*lem0kyI^W9dVE zRZTH~8aGTN^0pw_s^DSLVBRWfV)8l zrZJdo6hw{E>^*tpc>UbysUeh%7r#(xYO7f;y|ZsyE48avQqED=;q(YA5+^{h=Ozc# z7e|{nPd^T{wH}yje$+;cLoZR4Ac~BzON0!J4ud}inuO_ECJ(uzDW`J&^^8-zLhW~f zK}SL%eaPWa<3%LSeZE(Q<1-Xcd=$1_jPswHLHE9x_Bc z=tNO8Z#eU=eB9q)+&_A2{hkq(e6xU!0jfRL1(vwl$FmIr+7ia*!kb^XGVWCs|ptYglBK*1D#b{7JQoJAN8hzk z)W znzz0JgNr?C7XW;*wd%3B5h%GN$0`7dx(o7@l7i7O*860ZF9*QC{_F$biR%S`tN`H9 zJ0wNv#!!U(FAlmGh9)@A1}Z(6lhv0J)EkWc|FranYvUQmxQP8CDj#r$=mUBjqPh~w z3KX8^?zFzXu{n77$>9VIz#Wtdv7i5=-#@f)^s_IlEdKI;jHfEjms`?Pt+*uWK7?X6 zaA4ZOLJ$LGL0zFHU@d~yj~&!A{ikk5CA#(@39JDg2ipMM!MX;Q1oRmI4HKR58rjm(n@`WW{KtnItyNeqUi-_M&)<3C@*s%Pcz>_?i?#?09bFFK9odDd8$$bX*l7`!C9)s-2CNBOTk2j( z$xDqH2gRoLf-2Tvc>)?@A+K?eBFs+RD}$X^Oo_~5QEh!yCo6llb=8Cw4-{jnBxJaq zuiVys1#6WMAjZ~R4N9D-N8}JRF=J3z@mN~aiLE`@txF$i!Q??eWY!iFFRkSX4azHQ z4vSV9nk!h3(|89ldJi_^HyWeMm*Y#HYL8F^T2{uoDuYykT2@H~gp!mDVw(`8f?2!8 z^W*)s3X*GM)7OsPeP3y@b1f^_ef#UdX8F~H?#Wr!!rhaz?AfPZpJD&oJvqy&aB6dg z_1g5w8CFu=le4UAx+j7B*Ii|FPtLL*=$@QKV4vEYLFnE0_ia1ACIxB7`s-_y-IKFO zeBG0?h*DFVGl(wTle6d*-IHa8QNMd~mIUseoMm|Go}7hlcTdj3$o73~S{<3irdwkV tj9)!~kPKnffj&mBHFH>gco&*G{n^d+@%GlRe# zC@foGeEnSJ%XJ-0SIcFcy#xe57FF<2F7hhyLWP>sy2`V~z_}7) z4b_T)Hd+I<+=2{644Q<@@-&U8;KR;VdiS1Jui3}Aaoe~*8+~f3thP7>thZ7QLPP{i zs)S^iQZ3BaQ?cp3%j@9EKmON#Ozsp%7vAYy)Yy#La6-7*>VaR5a zos*9I1JrKsZSMQ$8twPIe&gbB3pp#-ZZ8*%#KtccmT8en;EoF1#uAj27!o4lNE;R# SE>L%pzvaOsn-^6%FX}&_>S}EO literal 0 HcmV?d00001 diff --git a/doc/reference_samples/pp-config/Props b/doc/reference_samples/pp-config/Props new file mode 100644 index 0000000000000000000000000000000000000000..3d0913ccf79dc70e0519101b63dd051c445b9301 GIT binary patch literal 17779 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 qb*7u=oT4nNYB^2qLe+8_^KP@{6glpy_s; literal 0 HcmV?d00001 diff --git a/doc/reference_samples/pp-config/Stage b/doc/reference_samples/pp-config/Stage new file mode 100644 index 0000000000000000000000000000000000000000..c9a3d1c0f311ab8c9fa58e7f51ebdeec9da0063d GIT binary patch literal 57284 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 literal 0 HcmV?d00001 diff --git a/doc/reference_samples/pp-config/TestPatterns b/doc/reference_samples/pp-config/TestPatterns new file mode 100644 index 0000000..ffd3001 --- /dev/null +++ b/doc/reference_samples/pp-config/TestPatterns @@ -0,0 +1,3 @@ + +*"& +$BCDE1115-AD40-4BA4-A33A-BFFE3E87223B \ No newline at end of file diff --git a/doc/reference_samples/pp-config/Timers b/doc/reference_samples/pp-config/Timers new file mode 100644 index 0000000000000000000000000000000000000000..93316f7aed106d92794b8d7735781e37b13a8a7e GIT binary patch literal 420 zcmYk&O-jQ+6u@y)u(k=+I*OEl5Q>N{24*HRNv5k#=EI_hQdceu4O*}Xq*J|&JHZQh zfnLQEh&M1P75Y|h_2d7Zd)jh9v$fxC_MD#6KWKq&qu&k@!36O*0(@_R5vo_$)*G~oh0d@yoY5)KL literal 0 HcmV?d00001 diff --git a/doc/reference_samples/pp-config/Workspace b/doc/reference_samples/pp-config/Workspace new file mode 100644 index 0000000000000000000000000000000000000000..df60a03e85c43c808131554af88ed04f7b561970 GIT binary patch literal 12739 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 BTPgqm literal 0 HcmV?d00001 diff --git a/doc/reference_samples/pp-themes/sample/Assets/BACKGROUND.jpg b/doc/reference_samples/pp-themes/sample/Assets/BACKGROUND.jpg new file mode 100644 index 0000000000000000000000000000000000000000..90faca52485bde2734e5692c4e5059c5be067200 GIT binary patch literal 142735 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 literal 0 HcmV?d00001 diff --git a/doc/reference_samples/pp-themes/sample/Assets/BAUCHBIND_STREAM.jpg b/doc/reference_samples/pp-themes/sample/Assets/BAUCHBIND_STREAM.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b59b39536062319e57f857ac321eec3cb5ad3d90 GIT binary patch literal 27812 zcmeI!J8#oa7{>AAG*ViGQ2harj*QqEA3JT@#%dbTRtScmh>KuN6VpT`wv`ygbU+;# zky!a03@i*}-~(_eg@PCuk+_X)#;(!}uylGPi|0sHZ7214{Tu#u{679X)#!v_;3(w{ z%x2qPcg>dHQ&BTewrMG`REvUUYts$oy1UW!s>Y9J&kfmaSB;zHx>XNW-A?z?R^Pq0 zb>(_%YqM2p8?|M*6gg2Z=(%B2M!h@Uz=^8H{>`1_IK8Y4x&M`Lvua#Oca^v54Y}&~ zU0F8gi!FQ3k_#2np10=8_KYl9_JXp?%9<})wqq?iC0mY{k?huQ-*&F9T^#R~oK=nS zLk)*Rb2w}I{S9STDivjwRH;-&Yt7V*>k)(dyY3}&++E$Io_N-$D6a~cysm~Z_b|M z&DnFjIeU&bXV3BG>^a_?J;$4~=Xi7W9B&Yt7V*>k)(dyY3}&++E$Io_N- h$D6a~cysm~Z_b|M&DnFjIeU&bXV3BG>^a_i?>9PMCW!z5 literal 0 HcmV?d00001 diff --git a/doc/reference_samples/pp-themes/sample/Assets/KEY_VISUAL.jpg b/doc/reference_samples/pp-themes/sample/Assets/KEY_VISUAL.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4849d6d8a6459fe857fe3a28cb10f9b94a92f7aa GIT binary patch literal 159459 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�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

h8d9Uh>liP|c6odzcz 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 HJoLW+04mNb literal 0 HcmV?d00001 diff --git a/doc/reference_samples/pp-themes/sample/Theme b/doc/reference_samples/pp-themes/sample/Theme new file mode 100644 index 0000000000000000000000000000000000000000..7d46a9b4593b45cca0bdfcc30f0c7593ec7a0a8f GIT binary patch literal 31038 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 literal 0 HcmV?d00001 diff --git a/generated/GPBMetadata/Calendar.php b/generated/GPBMetadata/Calendar.php new file mode 100644 index 0000000..49c4a2e --- /dev/null +++ b/generated/GPBMetadata/Calendar.php @@ -0,0 +1,27 @@ +internalAddGeneratedFile( + "\x0A\xF2\x02\x0A\x0Ecalendar.proto\x12\x07rv.data\x1A\x0Auuid.proto\"\x8C\x02\x0A\x10CalendarDocument\x12/\x0A\x06events\x18\x01 \x03(\x0B2\x1F.rv.data.CalendarDocument.Event\x12\x0C\x0A\x04mode\x18\x02 \x01(\x0D\x1A\xB8\x01\x0A\x05Event\x12\x1B\x0A\x04uuid\x18\x01 \x01(\x0B2\x0D.rv.data.UUID\x12\x0C\x0A\x04name\x18\x02 \x01(\x09\x12&\x0A\x0Astart_time\x18\x04 \x01(\x0B2\x12.rv.data.Timestamp\x12\x0D\x0A\x05flags\x18\x05 \x01(\x0C\x12\$\x0A\x08end_time\x18\x06 \x01(\x0B2\x12.rv.data.Timestamp\x12\x13\x0A\x0Baction_data\x18\x08 \x01(\x0C\x12\x12\x0A\x0Amacro_data\x18\x09 \x01(\x0CB4\xF8\x01\x01\xAA\x02\$Pro.SerializationInterop.RVProtoData\xBA\x02\x07RVData_b\x06proto3" + , true); + + static::$is_initialized = true; + } +} + diff --git a/generated/GPBMetadata/KeyMappings.php b/generated/GPBMetadata/KeyMappings.php new file mode 100644 index 0000000..60e986e --- /dev/null +++ b/generated/GPBMetadata/KeyMappings.php @@ -0,0 +1,28 @@ +internalAddGeneratedFile( + "\x0A\xE0\x02\x0A\x11keyMappings.proto\x12\x07rv.data\x1A\x0ChotKey.proto\x1A\x0Auuid.proto\"\xE9\x01\x0A\x13KeyMappingsDocument\x122\x0A\x10application_info\x18\x01 \x01(\x0B2\x18.rv.data.ApplicationInfo\x126\x0A\x08mappings\x18\x02 \x03(\x0B2\$.rv.data.KeyMappingsDocument.Mapping\x1Af\x0A\x07Mapping\x12\x1B\x0A\x04uuid\x18\x01 \x01(\x0B2\x0D.rv.data.UUID\x12 \x0A\x07hot_key\x18\x02 \x01(\x0B2\x0F.rv.data.HotKey\x12\x0E\x0A\x06target\x18\x03 \x01(\x0C\x12\x0C\x0A\x04name\x18\x04 \x01(\x09B4\xF8\x01\x01\xAA\x02\$Pro.SerializationInterop.RVProtoData\xBA\x02\x07RVData_b\x06proto3" + , true); + + static::$is_initialized = true; + } +} + diff --git a/generated/Rv/Data/CalendarDocument.php b/generated/Rv/Data/CalendarDocument.php new file mode 100644 index 0000000..0e10eca --- /dev/null +++ b/generated/Rv/Data/CalendarDocument.php @@ -0,0 +1,107 @@ +rv.data.CalendarDocument + */ +class CalendarDocument extends \Google\Protobuf\Internal\Message +{ + /** + * Events scheduled in the calendar, in the order ProPresenter wrote them. + * + * Generated from protobuf field repeated .rv.data.CalendarDocument.Event events = 1; + */ + private $events; + /** + * Source / mode flag observed in samples (value `1`). Treated as opaque. + * + * Generated from protobuf field uint32 mode = 2; + */ + protected $mode = 0; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type array<\Rv\Data\CalendarDocument\Event>|\Google\Protobuf\Internal\RepeatedField $events + * Events scheduled in the calendar, in the order ProPresenter wrote them. + * @type int $mode + * Source / mode flag observed in samples (value `1`). Treated as opaque. + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Calendar::initOnce(); + parent::__construct($data); + } + + /** + * Events scheduled in the calendar, in the order ProPresenter wrote them. + * + * Generated from protobuf field repeated .rv.data.CalendarDocument.Event events = 1; + * @return \Google\Protobuf\Internal\RepeatedField + */ + public function getEvents() + { + return $this->events; + } + + /** + * Events scheduled in the calendar, in the order ProPresenter wrote them. + * + * Generated from protobuf field repeated .rv.data.CalendarDocument.Event events = 1; + * @param array<\Rv\Data\CalendarDocument\Event>|\Google\Protobuf\Internal\RepeatedField $var + * @return $this + */ + public function setEvents($var) + { + $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Rv\Data\CalendarDocument\Event::class); + $this->events = $arr; + + return $this; + } + + /** + * Source / mode flag observed in samples (value `1`). Treated as opaque. + * + * Generated from protobuf field uint32 mode = 2; + * @return int + */ + public function getMode() + { + return $this->mode; + } + + /** + * Source / mode flag observed in samples (value `1`). Treated as opaque. + * + * Generated from protobuf field uint32 mode = 2; + * @param int $var + * @return $this + */ + public function setMode($var) + { + GPBUtil::checkUint32($var); + $this->mode = $var; + + return $this; + } + +} + diff --git a/generated/Rv/Data/CalendarDocument/Event.php b/generated/Rv/Data/CalendarDocument/Event.php new file mode 100644 index 0000000..21dd3b2 --- /dev/null +++ b/generated/Rv/Data/CalendarDocument/Event.php @@ -0,0 +1,316 @@ +rv.data.CalendarDocument.Event + */ +class Event extends \Google\Protobuf\Internal\Message +{ + /** + * Stable identity of this calendar event. + * + * Generated from protobuf field .rv.data.UUID uuid = 1; + */ + protected $uuid = null; + /** + * Display name (e.g. "Doors Open"). + * + * Generated from protobuf field string name = 2; + */ + protected $name = ''; + /** + * When the event starts. Stored as Timestamp seconds (and optional nanos). + * + * Generated from protobuf field .rv.data.Timestamp start_time = 4; + */ + protected $start_time = null; + /** + * Opaque flags blob observed in samples. Often a single byte (e.g. 0x01) + * that ProPresenter uses for recurrence or visibility state. + * + * Generated from protobuf field bytes flags = 5; + */ + protected $flags = ''; + /** + * When the event ends. Optional. Same format as `start_time`. + * + * Generated from protobuf field .rv.data.Timestamp end_time = 6; + */ + protected $end_time = null; + /** + * Action that runs when the event fires. Encoded as raw protobuf bytes + * (a `rv.data.Action`-shaped message) so the schema can evolve without + * breaking existing clients. + * + * Generated from protobuf field bytes action_data = 8; + */ + protected $action_data = ''; + /** + * Embedded copy of the macro definition the event triggers. Stored as + * raw protobuf bytes (shape compatible with `MacrosDocument.Macro`). + * + * Generated from protobuf field bytes macro_data = 9; + */ + protected $macro_data = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type \Rv\Data\UUID $uuid + * Stable identity of this calendar event. + * @type string $name + * Display name (e.g. "Doors Open"). + * @type \Rv\Data\Timestamp $start_time + * When the event starts. Stored as Timestamp seconds (and optional nanos). + * @type string $flags + * Opaque flags blob observed in samples. Often a single byte (e.g. 0x01) + * that ProPresenter uses for recurrence or visibility state. + * @type \Rv\Data\Timestamp $end_time + * When the event ends. Optional. Same format as `start_time`. + * @type string $action_data + * Action that runs when the event fires. Encoded as raw protobuf bytes + * (a `rv.data.Action`-shaped message) so the schema can evolve without + * breaking existing clients. + * @type string $macro_data + * Embedded copy of the macro definition the event triggers. Stored as + * raw protobuf bytes (shape compatible with `MacrosDocument.Macro`). + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\Calendar::initOnce(); + parent::__construct($data); + } + + /** + * Stable identity of this calendar event. + * + * Generated from protobuf field .rv.data.UUID uuid = 1; + * @return \Rv\Data\UUID|null + */ + public function getUuid() + { + return $this->uuid; + } + + public function hasUuid() + { + return isset($this->uuid); + } + + public function clearUuid() + { + unset($this->uuid); + } + + /** + * Stable identity of this calendar event. + * + * Generated from protobuf field .rv.data.UUID uuid = 1; + * @param \Rv\Data\UUID $var + * @return $this + */ + public function setUuid($var) + { + GPBUtil::checkMessage($var, \Rv\Data\UUID::class); + $this->uuid = $var; + + return $this; + } + + /** + * Display name (e.g. "Doors Open"). + * + * Generated from protobuf field string name = 2; + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Display name (e.g. "Doors Open"). + * + * Generated from protobuf field string name = 2; + * @param string $var + * @return $this + */ + public function setName($var) + { + GPBUtil::checkString($var, True); + $this->name = $var; + + return $this; + } + + /** + * When the event starts. Stored as Timestamp seconds (and optional nanos). + * + * Generated from protobuf field .rv.data.Timestamp start_time = 4; + * @return \Rv\Data\Timestamp|null + */ + public function getStartTime() + { + return $this->start_time; + } + + public function hasStartTime() + { + return isset($this->start_time); + } + + public function clearStartTime() + { + unset($this->start_time); + } + + /** + * When the event starts. Stored as Timestamp seconds (and optional nanos). + * + * Generated from protobuf field .rv.data.Timestamp start_time = 4; + * @param \Rv\Data\Timestamp $var + * @return $this + */ + public function setStartTime($var) + { + GPBUtil::checkMessage($var, \Rv\Data\Timestamp::class); + $this->start_time = $var; + + return $this; + } + + /** + * Opaque flags blob observed in samples. Often a single byte (e.g. 0x01) + * that ProPresenter uses for recurrence or visibility state. + * + * Generated from protobuf field bytes flags = 5; + * @return string + */ + public function getFlags() + { + return $this->flags; + } + + /** + * Opaque flags blob observed in samples. Often a single byte (e.g. 0x01) + * that ProPresenter uses for recurrence or visibility state. + * + * Generated from protobuf field bytes flags = 5; + * @param string $var + * @return $this + */ + public function setFlags($var) + { + GPBUtil::checkString($var, False); + $this->flags = $var; + + return $this; + } + + /** + * When the event ends. Optional. Same format as `start_time`. + * + * Generated from protobuf field .rv.data.Timestamp end_time = 6; + * @return \Rv\Data\Timestamp|null + */ + public function getEndTime() + { + return $this->end_time; + } + + public function hasEndTime() + { + return isset($this->end_time); + } + + public function clearEndTime() + { + unset($this->end_time); + } + + /** + * When the event ends. Optional. Same format as `start_time`. + * + * Generated from protobuf field .rv.data.Timestamp end_time = 6; + * @param \Rv\Data\Timestamp $var + * @return $this + */ + public function setEndTime($var) + { + GPBUtil::checkMessage($var, \Rv\Data\Timestamp::class); + $this->end_time = $var; + + return $this; + } + + /** + * Action that runs when the event fires. Encoded as raw protobuf bytes + * (a `rv.data.Action`-shaped message) so the schema can evolve without + * breaking existing clients. + * + * Generated from protobuf field bytes action_data = 8; + * @return string + */ + public function getActionData() + { + return $this->action_data; + } + + /** + * Action that runs when the event fires. Encoded as raw protobuf bytes + * (a `rv.data.Action`-shaped message) so the schema can evolve without + * breaking existing clients. + * + * Generated from protobuf field bytes action_data = 8; + * @param string $var + * @return $this + */ + public function setActionData($var) + { + GPBUtil::checkString($var, False); + $this->action_data = $var; + + return $this; + } + + /** + * Embedded copy of the macro definition the event triggers. Stored as + * raw protobuf bytes (shape compatible with `MacrosDocument.Macro`). + * + * Generated from protobuf field bytes macro_data = 9; + * @return string + */ + public function getMacroData() + { + return $this->macro_data; + } + + /** + * Embedded copy of the macro definition the event triggers. Stored as + * raw protobuf bytes (shape compatible with `MacrosDocument.Macro`). + * + * Generated from protobuf field bytes macro_data = 9; + * @param string $var + * @return $this + */ + public function setMacroData($var) + { + GPBUtil::checkString($var, False); + $this->macro_data = $var; + + return $this; + } + +} + diff --git a/generated/Rv/Data/KeyMappingsDocument.php b/generated/Rv/Data/KeyMappingsDocument.php new file mode 100644 index 0000000..2b4b076 --- /dev/null +++ b/generated/Rv/Data/KeyMappingsDocument.php @@ -0,0 +1,115 @@ +rv.data.KeyMappingsDocument + */ +class KeyMappingsDocument extends \Google\Protobuf\Internal\Message +{ + /** + * Application metadata of the writer. + * + * Generated from protobuf field .rv.data.ApplicationInfo application_info = 1; + */ + protected $application_info = null; + /** + * Configured key bindings. Empty in the reference sample. + * + * Generated from protobuf field repeated .rv.data.KeyMappingsDocument.Mapping mappings = 2; + */ + private $mappings; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type \Rv\Data\ApplicationInfo $application_info + * Application metadata of the writer. + * @type array<\Rv\Data\KeyMappingsDocument\Mapping>|\Google\Protobuf\Internal\RepeatedField $mappings + * Configured key bindings. Empty in the reference sample. + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\KeyMappings::initOnce(); + parent::__construct($data); + } + + /** + * Application metadata of the writer. + * + * Generated from protobuf field .rv.data.ApplicationInfo application_info = 1; + * @return \Rv\Data\ApplicationInfo|null + */ + public function getApplicationInfo() + { + return $this->application_info; + } + + public function hasApplicationInfo() + { + return isset($this->application_info); + } + + public function clearApplicationInfo() + { + unset($this->application_info); + } + + /** + * Application metadata of the writer. + * + * Generated from protobuf field .rv.data.ApplicationInfo application_info = 1; + * @param \Rv\Data\ApplicationInfo $var + * @return $this + */ + public function setApplicationInfo($var) + { + GPBUtil::checkMessage($var, \Rv\Data\ApplicationInfo::class); + $this->application_info = $var; + + return $this; + } + + /** + * Configured key bindings. Empty in the reference sample. + * + * Generated from protobuf field repeated .rv.data.KeyMappingsDocument.Mapping mappings = 2; + * @return \Google\Protobuf\Internal\RepeatedField + */ + public function getMappings() + { + return $this->mappings; + } + + /** + * Configured key bindings. Empty in the reference sample. + * + * Generated from protobuf field repeated .rv.data.KeyMappingsDocument.Mapping mappings = 2; + * @param array<\Rv\Data\KeyMappingsDocument\Mapping>|\Google\Protobuf\Internal\RepeatedField $var + * @return $this + */ + public function setMappings($var) + { + $arr = GPBUtil::checkRepeatedField($var, \Google\Protobuf\Internal\GPBType::MESSAGE, \Rv\Data\KeyMappingsDocument\Mapping::class); + $this->mappings = $arr; + + return $this; + } + +} + diff --git a/generated/Rv/Data/KeyMappingsDocument/Mapping.php b/generated/Rv/Data/KeyMappingsDocument/Mapping.php new file mode 100644 index 0000000..813a543 --- /dev/null +++ b/generated/Rv/Data/KeyMappingsDocument/Mapping.php @@ -0,0 +1,196 @@ +rv.data.KeyMappingsDocument.Mapping + */ +class Mapping extends \Google\Protobuf\Internal\Message +{ + /** + * Optional stable identifier for the mapping. + * + * Generated from protobuf field .rv.data.UUID uuid = 1; + */ + protected $uuid = null; + /** + * The hot key combo that fires the action. + * + * Generated from protobuf field .rv.data.HotKey hot_key = 2; + */ + protected $hot_key = null; + /** + * Action target — typically a macro UUID, a control identifier, or any + * other reference ProPresenter chooses to encode. Kept as bytes so we + * round-trip cleanly while ProPresenter's internal schema evolves. + * + * Generated from protobuf field bytes target = 3; + */ + protected $target = ''; + /** + * Display name (optional). + * + * Generated from protobuf field string name = 4; + */ + protected $name = ''; + + /** + * Constructor. + * + * @param array $data { + * Optional. Data for populating the Message object. + * + * @type \Rv\Data\UUID $uuid + * Optional stable identifier for the mapping. + * @type \Rv\Data\HotKey $hot_key + * The hot key combo that fires the action. + * @type string $target + * Action target — typically a macro UUID, a control identifier, or any + * other reference ProPresenter chooses to encode. Kept as bytes so we + * round-trip cleanly while ProPresenter's internal schema evolves. + * @type string $name + * Display name (optional). + * } + */ + public function __construct($data = NULL) { + \GPBMetadata\KeyMappings::initOnce(); + parent::__construct($data); + } + + /** + * Optional stable identifier for the mapping. + * + * Generated from protobuf field .rv.data.UUID uuid = 1; + * @return \Rv\Data\UUID|null + */ + public function getUuid() + { + return $this->uuid; + } + + public function hasUuid() + { + return isset($this->uuid); + } + + public function clearUuid() + { + unset($this->uuid); + } + + /** + * Optional stable identifier for the mapping. + * + * Generated from protobuf field .rv.data.UUID uuid = 1; + * @param \Rv\Data\UUID $var + * @return $this + */ + public function setUuid($var) + { + GPBUtil::checkMessage($var, \Rv\Data\UUID::class); + $this->uuid = $var; + + return $this; + } + + /** + * The hot key combo that fires the action. + * + * Generated from protobuf field .rv.data.HotKey hot_key = 2; + * @return \Rv\Data\HotKey|null + */ + public function getHotKey() + { + return $this->hot_key; + } + + public function hasHotKey() + { + return isset($this->hot_key); + } + + public function clearHotKey() + { + unset($this->hot_key); + } + + /** + * The hot key combo that fires the action. + * + * Generated from protobuf field .rv.data.HotKey hot_key = 2; + * @param \Rv\Data\HotKey $var + * @return $this + */ + public function setHotKey($var) + { + GPBUtil::checkMessage($var, \Rv\Data\HotKey::class); + $this->hot_key = $var; + + return $this; + } + + /** + * Action target — typically a macro UUID, a control identifier, or any + * other reference ProPresenter chooses to encode. Kept as bytes so we + * round-trip cleanly while ProPresenter's internal schema evolves. + * + * Generated from protobuf field bytes target = 3; + * @return string + */ + public function getTarget() + { + return $this->target; + } + + /** + * Action target — typically a macro UUID, a control identifier, or any + * other reference ProPresenter chooses to encode. Kept as bytes so we + * round-trip cleanly while ProPresenter's internal schema evolves. + * + * Generated from protobuf field bytes target = 3; + * @param string $var + * @return $this + */ + public function setTarget($var) + { + GPBUtil::checkString($var, False); + $this->target = $var; + + return $this; + } + + /** + * Display name (optional). + * + * Generated from protobuf field string name = 4; + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Display name (optional). + * + * Generated from protobuf field string name = 4; + * @param string $var + * @return $this + */ + public function setName($var) + { + GPBUtil::checkString($var, True); + $this->name = $var; + + return $this; + } + +} + diff --git a/proto/calendar.proto b/proto/calendar.proto new file mode 100644 index 0000000..a0c4163 --- /dev/null +++ b/proto/calendar.proto @@ -0,0 +1,51 @@ +syntax = "proto3"; + +package rv.data; + +option cc_enable_arenas = true; +option csharp_namespace = "Pro.SerializationInterop.RVProtoData"; +option swift_prefix = "RVData_"; + +import "rvtimestamp.proto"; +import "uuid.proto"; + +// CalendarDocument is the global ProPresenter `Calendar` file. It holds +// scheduled events that fire macros at given times. The exact semantics of +// the inner sub-messages 8 and 9 (action and embedded macro reference) are +// preserved as raw bytes so the document round-trips byte-for-byte; clients +// that need to inspect them can decode them with `protoc --decode_raw` or +// reach for the raw protobuf message. +message CalendarDocument { + message Event { + // Stable identity of this calendar event. + .rv.data.UUID uuid = 1; + + // Display name (e.g. "Doors Open"). + string name = 2; + + // When the event starts. Stored as Timestamp seconds (and optional nanos). + .rv.data.Timestamp start_time = 4; + + // Opaque flags blob observed in samples. Often a single byte (e.g. 0x01) + // that ProPresenter uses for recurrence or visibility state. + bytes flags = 5; + + // When the event ends. Optional. Same format as `start_time`. + .rv.data.Timestamp end_time = 6; + + // Action that runs when the event fires. Encoded as raw protobuf bytes + // (a `rv.data.Action`-shaped message) so the schema can evolve without + // breaking existing clients. + bytes action_data = 8; + + // Embedded copy of the macro definition the event triggers. Stored as + // raw protobuf bytes (shape compatible with `MacrosDocument.Macro`). + bytes macro_data = 9; + } + + // Events scheduled in the calendar, in the order ProPresenter wrote them. + repeated Event events = 1; + + // Source / mode flag observed in samples (value `1`). Treated as opaque. + uint32 mode = 2; +} diff --git a/proto/keyMappings.proto b/proto/keyMappings.proto new file mode 100644 index 0000000..cfc3414 --- /dev/null +++ b/proto/keyMappings.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; + +package rv.data; + +option cc_enable_arenas = true; +option csharp_namespace = "Pro.SerializationInterop.RVProtoData"; +option swift_prefix = "RVData_"; + +import "applicationInfo.proto"; +import "hotKey.proto"; +import "uuid.proto"; + +// KeyMappingsDocument is the global ProPresenter `KeyMappings` file. The +// reference sample on disk is just an `ApplicationInfo` envelope with an +// otherwise empty body — ProPresenter seeds the file at startup. Once the +// user binds hot keys we expect them in `mappings`. +message KeyMappingsDocument { + message Mapping { + // Optional stable identifier for the mapping. + .rv.data.UUID uuid = 1; + + // The hot key combo that fires the action. + .rv.data.HotKey hot_key = 2; + + // Action target — typically a macro UUID, a control identifier, or any + // other reference ProPresenter chooses to encode. Kept as bytes so we + // round-trip cleanly while ProPresenter's internal schema evolves. + bytes target = 3; + + // Display name (optional). + string name = 4; + } + + // Application metadata of the writer. + .rv.data.ApplicationInfo application_info = 1; + + // Configured key bindings. Empty in the reference sample. + repeated Mapping mappings = 2; +} diff --git a/src/CCLIFileReader.php b/src/CCLIFileReader.php new file mode 100644 index 0000000..b95d1a7 --- /dev/null +++ b/src/CCLIFileReader.php @@ -0,0 +1,38 @@ +mergeFromString($data); + + return new CCLILibrary($document); + } +} diff --git a/src/CCLIFileWriter.php b/src/CCLIFileWriter.php new file mode 100644 index 0000000..677130a --- /dev/null +++ b/src/CCLIFileWriter.php @@ -0,0 +1,26 @@ +getDocument()->serializeToString(); + $writtenBytes = file_put_contents($filePath, $data); + + if ($writtenBytes === false) { + throw new RuntimeException(sprintf('Unable to write CCLI file: %s', $filePath)); + } + } +} diff --git a/src/CCLILibrary.php b/src/CCLILibrary.php new file mode 100644 index 0000000..bff728b --- /dev/null +++ b/src/CCLILibrary.php @@ -0,0 +1,103 @@ +document->getEnableCcliDisplay(); + } + + public function setCCLIDisplayEnabled(bool $enabled): self + { + $this->document->setEnableCcliDisplay($enabled); + + return $this; + } + + public function getCCLILicense(): string + { + return $this->document->getCcliLicense(); + } + + public function setCCLILicense(string $license): self + { + $this->document->setCcliLicense($license); + + return $this; + } + + public function getDisplayType(): int + { + return $this->document->getDisplayType(); + } + + public function setDisplayType(int $displayType): self + { + $this->document->setDisplayType($displayType); + + return $this; + } + + public function getTemplate(): ?Slide + { + if (!$this->document->hasTemplate()) { + return null; + } + + return $this->document->getTemplate(); + } + + public function setTemplate(?Slide $template): self + { + if ($template === null) { + $this->document->clearTemplate(); + + return $this; + } + + $this->document->setTemplate($template); + + return $this; + } + + public function getApplicationInfo(): ?ApplicationInfo + { + return $this->document->getApplicationInfo(); + } + + public function setApplicationInfo(?ApplicationInfo $applicationInfo): self + { + if ($applicationInfo === null) { + $this->document->clearApplicationInfo(); + + return $this; + } + + $this->document->setApplicationInfo($applicationInfo); + + return $this; + } + + public function getDocument(): CCLIDocument + { + return $this->document; + } +} diff --git a/src/CalendarEvent.php b/src/CalendarEvent.php new file mode 100644 index 0000000..57d6fc6 --- /dev/null +++ b/src/CalendarEvent.php @@ -0,0 +1,140 @@ +event->getUuid()?->getString() ?? ''; + } + + public function setUuid(string $uuid): self + { + $proto = new UUID(); + $proto->setString($uuid); + $this->event->setUuid($proto); + + return $this; + } + + public function getName(): string + { + return $this->event->getName(); + } + + public function setName(string $name): self + { + $this->event->setName($name); + + return $this; + } + + public function getStartTime(): ?Timestamp + { + return $this->event->getStartTime(); + } + + public function setStartTime(Timestamp $timestamp): self + { + $this->event->setStartTime($timestamp); + + return $this; + } + + public function getStartTimeSeconds(): ?int + { + $seconds = $this->event->getStartTime()?->getSeconds(); + + return $seconds === null ? null : (int) $seconds; + } + + public function setStartTimeSeconds(int $seconds): self + { + $timestamp = new Timestamp(); + $timestamp->setSeconds($seconds); + $this->event->setStartTime($timestamp); + + return $this; + } + + public function getEndTime(): ?Timestamp + { + return $this->event->getEndTime(); + } + + public function setEndTime(Timestamp $timestamp): self + { + $this->event->setEndTime($timestamp); + + return $this; + } + + public function getEndTimeSeconds(): ?int + { + $seconds = $this->event->getEndTime()?->getSeconds(); + + return $seconds === null ? null : (int) $seconds; + } + + public function setEndTimeSeconds(int $seconds): self + { + $timestamp = new Timestamp(); + $timestamp->setSeconds($seconds); + $this->event->setEndTime($timestamp); + + return $this; + } + + public function getFlags(): string + { + return $this->event->getFlags(); + } + + public function setFlags(string $flags): self + { + $this->event->setFlags($flags); + + return $this; + } + + public function getActionData(): string + { + return $this->event->getActionData(); + } + + public function setActionData(string $actionData): self + { + $this->event->setActionData($actionData); + + return $this; + } + + public function getMacroData(): string + { + return $this->event->getMacroData(); + } + + public function setMacroData(string $macroData): self + { + $this->event->setMacroData($macroData); + + return $this; + } + + public function getProto(): EventProto + { + return $this->event; + } +} diff --git a/src/CalendarFileReader.php b/src/CalendarFileReader.php new file mode 100644 index 0000000..7c7cc1a --- /dev/null +++ b/src/CalendarFileReader.php @@ -0,0 +1,37 @@ +mergeFromString($data); + + return new CalendarLibrary($document); + } +} diff --git a/src/CalendarFileWriter.php b/src/CalendarFileWriter.php new file mode 100644 index 0000000..5a1c0ab --- /dev/null +++ b/src/CalendarFileWriter.php @@ -0,0 +1,24 @@ +getDocument()->serializeToString()); + if ($writtenBytes === false) { + throw new RuntimeException(sprintf('Unable to write Calendar file: %s', $filePath)); + } + } +} diff --git a/src/CalendarLibrary.php b/src/CalendarLibrary.php new file mode 100644 index 0000000..6332e42 --- /dev/null +++ b/src/CalendarLibrary.php @@ -0,0 +1,127 @@ + */ + private array $eventsByUuid = []; + + /** @var array */ + private array $eventsByName = []; + + public function __construct( + private readonly CalendarDocument $document, + ) { + $this->rebuildIndex(); + } + + /** + * @return CalendarEvent[] + */ + public function getEvents(): array + { + return $this->events; + } + + public function count(): int + { + return count($this->events); + } + + public function getEventByUuid(string $uuid): ?CalendarEvent + { + return $this->eventsByUuid[strtoupper($uuid)] ?? null; + } + + public function getEventByName(string $name): ?CalendarEvent + { + return $this->eventsByName[$name] ?? null; + } + + public function addEvent(string $name, string $uuid): CalendarEvent + { + $proto = new EventProto(); + $uuidProto = new UUID(); + $uuidProto->setString($uuid); + $proto->setUuid($uuidProto); + $proto->setName($name); + + $existing = iterator_to_array($this->document->getEvents()); + $existing[] = $proto; + $this->document->setEvents($existing); + $this->rebuildIndex(); + + return $this->getEventByUuid($uuid) ?? new CalendarEvent($proto); + } + + public function removeEvent(string $uuid): bool + { + $needle = strtoupper($uuid); + $kept = []; + $removed = false; + foreach ($this->document->getEvents() as $proto) { + $current = strtoupper($proto->getUuid()?->getString() ?? ''); + if (!$removed && $current === $needle) { + $removed = true; + continue; + } + $kept[] = $proto; + } + + if (!$removed) { + return false; + } + + $this->document->setEvents($kept); + $this->rebuildIndex(); + + return true; + } + + public function getMode(): int + { + return $this->document->getMode(); + } + + public function setMode(int $mode): void + { + $this->document->setMode($mode); + } + + public function getDocument(): CalendarDocument + { + return $this->document; + } + + private function rebuildIndex(): void + { + $this->events = []; + $this->eventsByUuid = []; + $this->eventsByName = []; + + foreach ($this->document->getEvents() as $proto) { + $event = new CalendarEvent($proto); + $this->events[] = $event; + + $uuid = strtoupper($event->getUuid()); + if ($uuid !== '') { + $this->eventsByUuid[$uuid] = $event; + } + + $name = $event->getName(); + if ($name !== '') { + $this->eventsByName[$name] ??= $event; + } + } + } +} diff --git a/src/ClearGroupDefinition.php b/src/ClearGroupDefinition.php new file mode 100644 index 0000000..5f894c0 --- /dev/null +++ b/src/ClearGroupDefinition.php @@ -0,0 +1,199 @@ +group->getUuid()?->getString() ?? ''; + } + + public function setUuid(string $uuid): self + { + $proto = new UUID(); + $proto->setString($uuid); + $this->group->setUuid($proto); + + return $this; + } + + public function getName(): string + { + return $this->group->getName(); + } + + public function setName(string $name): self + { + $this->group->setName($name); + + return $this; + } + + /** + * @return int[] + */ + public function getLayerTargets(): array + { + return iterator_to_array($this->group->getLayerTargets()); + } + + /** + * @param int[] $targets + */ + public function setLayerTargets(array $targets): self + { + $this->group->setLayerTargets($targets); + + return $this; + } + + public function isHiddenInPreview(): bool + { + return $this->group->getIsHiddenInPreview(); + } + + public function setHiddenInPreview(bool $hidden): self + { + $this->group->setIsHiddenInPreview($hidden); + + return $this; + } + + public function getImageData(): string + { + return $this->group->getImageData(); + } + + public function setImageData(string $imageData): self + { + $this->group->setImageData($imageData); + + return $this; + } + + public function getImageType(): int + { + return $this->group->getImageType(); + } + + public function setImageType(int $imageType): self + { + $this->group->setImageType($imageType); + + return $this; + } + + public function isIconTinted(): bool + { + return $this->group->getIsIconTinted(); + } + + public function setIconTinted(bool $tinted): self + { + $this->group->setIsIconTinted($tinted); + + return $this; + } + + /** + * @return array{r: float, g: float, b: float, a: float}|null + */ + public function getColor(): ?array + { + if (!$this->group->hasIconTintColor()) { + return null; + } + + $color = $this->group->getIconTintColor(); + + return [ + 'r' => $color->getRed(), + 'g' => $color->getGreen(), + 'b' => $color->getBlue(), + 'a' => $color->getAlpha(), + ]; + } + + public function getColorHex(): ?string + { + $color = $this->getColor(); + if ($color === null) { + return null; + } + + return sprintf( + '#%02X%02X%02X', + (int) round(max(0.0, min(1.0, $color['r'])) * 255), + (int) round(max(0.0, min(1.0, $color['g'])) * 255), + (int) round(max(0.0, min(1.0, $color['b'])) * 255), + ); + } + + /** + * @param array{r: float, g: float, b: float, a?: float}|null $color + */ + public function setColor(?array $color): self + { + if ($color === null) { + $this->group->clearIconTintColor(); + + return $this; + } + + $proto = new Color(); + $proto->setRed((float) $color['r']); + $proto->setGreen((float) $color['g']); + $proto->setBlue((float) $color['b']); + $proto->setAlpha((float) ($color['a'] ?? 1.0)); + $this->group->setIconTintColor($proto); + + return $this; + } + + /** + * @return int[] + */ + public function getTimelineTargets(): array + { + return iterator_to_array($this->group->getTimelineTargets()); + } + + /** + * @param int[] $targets + */ + public function setTimelineTargets(array $targets): self + { + $this->group->setTimelineTargets($targets); + + return $this; + } + + public function clearsPresentationNextSlide(): bool + { + return $this->group->getClearPresentationNextSlide(); + } + + public function setClearPresentationNextSlide(bool $clear): self + { + $this->group->setClearPresentationNextSlide($clear); + + return $this; + } + + public function getProto(): ClearGroupProto + { + return $this->group; + } +} diff --git a/src/ClearGroupsFileReader.php b/src/ClearGroupsFileReader.php new file mode 100644 index 0000000..bb06635 --- /dev/null +++ b/src/ClearGroupsFileReader.php @@ -0,0 +1,38 @@ +mergeFromString($data); + + return new ClearGroupsLibrary($document); + } +} diff --git a/src/ClearGroupsFileWriter.php b/src/ClearGroupsFileWriter.php new file mode 100644 index 0000000..06e6608 --- /dev/null +++ b/src/ClearGroupsFileWriter.php @@ -0,0 +1,26 @@ +getDocument()->serializeToString(); + $writtenBytes = file_put_contents($filePath, $data); + + if ($writtenBytes === false) { + throw new RuntimeException(sprintf('Unable to write ClearGroups file: %s', $filePath)); + } + } +} diff --git a/src/ClearGroupsLibrary.php b/src/ClearGroupsLibrary.php new file mode 100644 index 0000000..7678275 --- /dev/null +++ b/src/ClearGroupsLibrary.php @@ -0,0 +1,119 @@ + */ + private array $groupsByUuid = []; + + /** @var array */ + private array $groupsByName = []; + + public function __construct( + private readonly ClearGroupsDocument $document, + ) { + $this->rebuildIndex(); + } + + /** + * Return clear groups in document order. + * + * @return ClearGroupDefinition[] + */ + public function getGroups(): array + { + return $this->groups; + } + + public function count(): int + { + return count($this->groups); + } + + public function getClearGroupByUuid(string $uuid): ?ClearGroupDefinition + { + return $this->groupsByUuid[strtoupper($uuid)] ?? null; + } + + public function getClearGroupByName(string $name): ?ClearGroupDefinition + { + return $this->groupsByName[$name] ?? null; + } + + public function addClearGroup(string $name, string $uuid): ClearGroupDefinition + { + $proto = new ClearGroupProto(); + $uuidProto = new UUID(); + $uuidProto->setString($uuid); + $proto->setUuid($uuidProto); + $proto->setName($name); + + $existing = iterator_to_array($this->document->getGroups()); + $existing[] = $proto; + $this->document->setGroups($existing); + $this->rebuildIndex(); + + return $this->getClearGroupByUuid($uuid) ?? new ClearGroupDefinition($proto); + } + + public function removeClearGroup(string $uuid): bool + { + $needle = strtoupper($uuid); + $kept = []; + $removed = false; + foreach ($this->document->getGroups() as $proto) { + $current = strtoupper($proto->getUuid()?->getString() ?? ''); + if (!$removed && $current === $needle) { + $removed = true; + continue; + } + $kept[] = $proto; + } + + if (!$removed) { + return false; + } + + $this->document->setGroups($kept); + $this->rebuildIndex(); + + return true; + } + + public function getDocument(): ClearGroupsDocument + { + return $this->document; + } + + private function rebuildIndex(): void + { + $this->groups = []; + $this->groupsByUuid = []; + $this->groupsByName = []; + + foreach ($this->document->getGroups() as $proto) { + $group = new ClearGroupDefinition($proto); + $this->groups[] = $group; + + $uuid = strtoupper($group->getUuid()); + if ($uuid !== '') { + $this->groupsByUuid[$uuid] = $group; + } + + $name = $group->getName(); + if ($name !== '') { + $this->groupsByName[$name] ??= $group; + } + } + } +} diff --git a/src/CommunicationDevice.php b/src/CommunicationDevice.php new file mode 100644 index 0000000..cd1f248 --- /dev/null +++ b/src/CommunicationDevice.php @@ -0,0 +1,75 @@ + */ + private array $fields; + + /** + * @param array|null $fields + */ + public function __construct(?array $fields = null) + { + $this->fields = $fields ?? []; + } + + public function getId(): string + { + return (string) ($this->fields['id'] ?? ''); + } + + public function setId(string $id): self + { + $this->fields['id'] = $id; + + return $this; + } + + public function getName(): string + { + return (string) ($this->fields['name'] ?? ''); + } + + public function setName(string $name): self + { + $this->fields['name'] = $name; + + return $this; + } + + public function getType(): string + { + return (string) ($this->fields['type'] ?? ''); + } + + public function setType(string $type): self + { + $this->fields['type'] = $type; + + return $this; + } + + public function getAddress(): string + { + return (string) ($this->fields['address'] ?? ''); + } + + public function setAddress(string $address): self + { + $this->fields['address'] = $address; + + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + return $this->fields; + } +} diff --git a/src/CommunicationDevicesFileReader.php b/src/CommunicationDevicesFileReader.php new file mode 100644 index 0000000..cb8e374 --- /dev/null +++ b/src/CommunicationDevicesFileReader.php @@ -0,0 +1,25 @@ +toJson(JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + $writtenBytes = file_put_contents($filePath, $json); + if ($writtenBytes === false) { + throw new RuntimeException(sprintf('Unable to write CommunicationDevices file: %s', $filePath)); + } + } +} diff --git a/src/CommunicationDevicesLibrary.php b/src/CommunicationDevicesLibrary.php new file mode 100644 index 0000000..7996eae --- /dev/null +++ b/src/CommunicationDevicesLibrary.php @@ -0,0 +1,94 @@ +devices = array_values($devices); + } + + public static function fromJson(string $json): self + { + $decoded = json_decode($json, true); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new RuntimeException('Unable to decode CommunicationDevices JSON: ' . json_last_error_msg()); + } + + if (!is_array($decoded)) { + throw new RuntimeException('CommunicationDevices JSON must decode to an array.'); + } + + $devices = []; + foreach ($decoded as $entry) { + if (is_array($entry)) { + /** @var array $entry */ + $devices[] = new CommunicationDevice($entry); + } + } + + return new self($devices); + } + + public function toJson(int $flags = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES): string + { + $json = json_encode($this->getDocument(), $flags); + if ($json === false) { + throw new RuntimeException('Unable to encode CommunicationDevices JSON: ' . json_last_error_msg()); + } + + return $json; + } + + /** + * @return array> + */ + public function getDocument(): array + { + return array_map(static fn (CommunicationDevice $device): array => $device->toArray(), $this->devices); + } + + /** + * @return CommunicationDevice[] + */ + public function getDevices(): array + { + return $this->devices; + } + + public function addDevice(CommunicationDevice $device): CommunicationDevice + { + $this->devices[] = $device; + + return $device; + } + + public function removeDevice(string $id): bool + { + foreach ($this->devices as $index => $device) { + if ($device->getId() === $id) { + array_splice($this->devices, $index, 1); + + return true; + } + } + + return false; + } + + public function count(): int + { + return count($this->devices); + } +} diff --git a/src/GroupDefinition.php b/src/GroupDefinition.php new file mode 100644 index 0000000..b17bad8 --- /dev/null +++ b/src/GroupDefinition.php @@ -0,0 +1,119 @@ +group->getUuid()?->getString() ?? ''; + } + + public function setUuid(string $uuid): self + { + $proto = new UUID(); + $proto->setString($uuid); + $this->group->setUuid($proto); + + return $this; + } + + public function getName(): string + { + return $this->group->getName(); + } + + public function setName(string $name): self + { + $this->group->setName($name); + + return $this; + } + + /** + * @return array{r: float, g: float, b: float, a: float}|null + */ + public function getColor(): ?array + { + if (!$this->group->hasColor()) { + return null; + } + + $color = $this->group->getColor(); + + return [ + 'r' => $color->getRed(), + 'g' => $color->getGreen(), + 'b' => $color->getBlue(), + 'a' => $color->getAlpha(), + ]; + } + + public function getColorHex(): ?string + { + $color = $this->getColor(); + if ($color === null) { + return null; + } + + return sprintf( + '#%02X%02X%02X', + (int) round(max(0.0, min(1.0, $color['r'])) * 255), + (int) round(max(0.0, min(1.0, $color['g'])) * 255), + (int) round(max(0.0, min(1.0, $color['b'])) * 255), + ); + } + + /** + * @param array{r: float, g: float, b: float, a?: float}|null $color + */ + public function setColor(?array $color): self + { + if ($color === null) { + $this->group->clearColor(); + + return $this; + } + + $proto = new Color(); + $proto->setRed((float) $color['r']); + $proto->setGreen((float) $color['g']); + $proto->setBlue((float) $color['b']); + $proto->setAlpha((float) ($color['a'] ?? 1.0)); + $this->group->setColor($proto); + + return $this; + } + + public function getHotKey(): ?HotKey + { + return $this->group->getHotKey(); + } + + public function getApplicationGroupName(): string + { + return $this->group->getApplicationGroupName(); + } + + public function getApplicationGroupUuid(): string + { + return $this->group->getApplicationGroupIdentifier()?->getString() ?? ''; + } + + public function getProto(): GroupProto + { + return $this->group; + } +} diff --git a/src/GroupLibrary.php b/src/GroupLibrary.php new file mode 100644 index 0000000..01543d1 --- /dev/null +++ b/src/GroupLibrary.php @@ -0,0 +1,117 @@ + */ + private array $groupsByUuid = []; + + /** @var array */ + private array $groupsByName = []; + + public function __construct( + private readonly ProGroupsDocument $document, + ) { + $this->rebuildIndex(); + } + + /** + * @return GroupDefinition[] + */ + public function getGroups(): array + { + return $this->groups; + } + + public function count(): int + { + return count($this->groups); + } + + public function getGroupByUuid(string $uuid): ?GroupDefinition + { + return $this->groupsByUuid[strtoupper($uuid)] ?? null; + } + + public function getGroupByName(string $name): ?GroupDefinition + { + return $this->groupsByName[$name] ?? null; + } + + public function addGroup(string $name, string $uuid): GroupDefinition + { + $proto = new GroupProto(); + $uuidProto = new UUID(); + $uuidProto->setString($uuid); + $proto->setUuid($uuidProto); + $proto->setName($name); + + $existing = iterator_to_array($this->document->getGroups()); + $existing[] = $proto; + $this->document->setGroups($existing); + $this->rebuildIndex(); + + return $this->getGroupByUuid($uuid) ?? new GroupDefinition($proto); + } + + public function removeGroup(string $uuid): bool + { + $needle = strtoupper($uuid); + $kept = []; + $removed = false; + foreach ($this->document->getGroups() as $proto) { + $current = strtoupper($proto->getUuid()?->getString() ?? ''); + if (!$removed && $current === $needle) { + $removed = true; + continue; + } + $kept[] = $proto; + } + + if (!$removed) { + return false; + } + + $this->document->setGroups($kept); + $this->rebuildIndex(); + + return true; + } + + public function getDocument(): ProGroupsDocument + { + return $this->document; + } + + private function rebuildIndex(): void + { + $this->groups = []; + $this->groupsByUuid = []; + $this->groupsByName = []; + + foreach ($this->document->getGroups() as $proto) { + $group = new GroupDefinition($proto); + $this->groups[] = $group; + + $uuid = strtoupper($group->getUuid()); + if ($uuid !== '') { + $this->groupsByUuid[$uuid] = $group; + } + + $name = $group->getName(); + if ($name !== '') { + $this->groupsByName[$name] ??= $group; + } + } + } +} diff --git a/src/GroupsFileReader.php b/src/GroupsFileReader.php new file mode 100644 index 0000000..e95d4ba --- /dev/null +++ b/src/GroupsFileReader.php @@ -0,0 +1,38 @@ +mergeFromString($data); + + return new GroupLibrary($document); + } +} diff --git a/src/GroupsFileWriter.php b/src/GroupsFileWriter.php new file mode 100644 index 0000000..482f6a7 --- /dev/null +++ b/src/GroupsFileWriter.php @@ -0,0 +1,26 @@ +getDocument()->serializeToString(); + $writtenBytes = file_put_contents($filePath, $data); + + if ($writtenBytes === false) { + throw new RuntimeException(sprintf('Unable to write Groups file: %s', $filePath)); + } + } +} diff --git a/src/KeyMapping.php b/src/KeyMapping.php new file mode 100644 index 0000000..e217f5b --- /dev/null +++ b/src/KeyMapping.php @@ -0,0 +1,82 @@ +mapping->getUuid()?->getString() ?? ''; + } + + public function setUuid(string $uuid): self + { + $proto = new UUID(); + $proto->setString($uuid); + $this->mapping->setUuid($proto); + + return $this; + } + + public function getName(): string + { + return $this->mapping->getName(); + } + + public function setName(string $name): self + { + $this->mapping->setName($name); + + return $this; + } + + public function getHotKey(): ?HotKey + { + if (!$this->mapping->hasHotKey()) { + return null; + } + + return $this->mapping->getHotKey(); + } + + public function setHotKey(?HotKey $hotKey): self + { + if ($hotKey === null) { + $this->mapping->clearHotKey(); + + return $this; + } + + $this->mapping->setHotKey($hotKey); + + return $this; + } + + public function getTarget(): string + { + return $this->mapping->getTarget(); + } + + public function setTarget(string $target): self + { + $this->mapping->setTarget($target); + + return $this; + } + + public function getProto(): MappingProto + { + return $this->mapping; + } +} diff --git a/src/KeyMappingsFileReader.php b/src/KeyMappingsFileReader.php new file mode 100644 index 0000000..0038d39 --- /dev/null +++ b/src/KeyMappingsFileReader.php @@ -0,0 +1,38 @@ +mergeFromString($data); + + return new KeyMappingsLibrary($document); + } +} diff --git a/src/KeyMappingsFileWriter.php b/src/KeyMappingsFileWriter.php new file mode 100644 index 0000000..dae0778 --- /dev/null +++ b/src/KeyMappingsFileWriter.php @@ -0,0 +1,26 @@ +getDocument()->serializeToString(); + $writtenBytes = file_put_contents($filePath, $data); + + if ($writtenBytes === false) { + throw new RuntimeException(sprintf('Unable to write KeyMappings file: %s', $filePath)); + } + } +} diff --git a/src/KeyMappingsLibrary.php b/src/KeyMappingsLibrary.php new file mode 100644 index 0000000..fff4441 --- /dev/null +++ b/src/KeyMappingsLibrary.php @@ -0,0 +1,139 @@ + */ + private array $mappingsByUuid = []; + + /** @var array */ + private array $mappingsByName = []; + + public function __construct( + private readonly KeyMappingsDocument $document, + ) { + $this->rebuildIndex(); + } + + /** + * Return key mappings in document order. + * + * @return KeyMapping[] + */ + public function getMappings(): array + { + return $this->mappings; + } + + public function count(): int + { + return count($this->mappings); + } + + public function getMappingByUuid(string $uuid): ?KeyMapping + { + return $this->mappingsByUuid[strtoupper($uuid)] ?? null; + } + + public function getMappingByName(string $name): ?KeyMapping + { + return $this->mappingsByName[$name] ?? null; + } + + public function addMapping(string $name, string $uuid, string $target = ''): KeyMapping + { + $proto = new MappingProto(); + $uuidProto = new UUID(); + $uuidProto->setString($uuid); + $proto->setUuid($uuidProto); + $proto->setName($name); + $proto->setTarget($target); + + $existing = iterator_to_array($this->document->getMappings()); + $existing[] = $proto; + $this->document->setMappings($existing); + $this->rebuildIndex(); + + return $this->getMappingByUuid($uuid) ?? new KeyMapping($proto); + } + + public function removeMapping(string $uuid): bool + { + $needle = strtoupper($uuid); + $kept = []; + $removed = false; + foreach ($this->document->getMappings() as $proto) { + $current = strtoupper($proto->getUuid()?->getString() ?? ''); + if (!$removed && $current === $needle) { + $removed = true; + continue; + } + $kept[] = $proto; + } + + if (!$removed) { + return false; + } + + $this->document->setMappings($kept); + $this->rebuildIndex(); + + return true; + } + + public function getApplicationInfo(): ?ApplicationInfo + { + return $this->document->getApplicationInfo(); + } + + public function setApplicationInfo(?ApplicationInfo $applicationInfo): self + { + if ($applicationInfo === null) { + $this->document->clearApplicationInfo(); + + return $this; + } + + $this->document->setApplicationInfo($applicationInfo); + + return $this; + } + + public function getDocument(): KeyMappingsDocument + { + return $this->document; + } + + private function rebuildIndex(): void + { + $this->mappings = []; + $this->mappingsByUuid = []; + $this->mappingsByName = []; + + foreach ($this->document->getMappings() as $proto) { + $mapping = new KeyMapping($proto); + $this->mappings[] = $mapping; + + $uuid = strtoupper($mapping->getUuid()); + if ($uuid !== '') { + $this->mappingsByUuid[$uuid] = $mapping; + } + + $name = $mapping->getName(); + if ($name !== '') { + $this->mappingsByName[$name] ??= $mapping; + } + } + } +} diff --git a/src/Label.php b/src/Label.php index 797e1fa..b673a5c 100644 --- a/src/Label.php +++ b/src/Label.php @@ -4,15 +4,10 @@ declare(strict_types=1); namespace ProPresenter\Parser; +use InvalidArgumentException; use Rv\Data\Action\Label as LabelProto; +use Rv\Data\Color; -/** - * Wraps a protobuf {@see LabelProto} from the global ProPresenter `Labels` - * file. Surfaces the label's display name (the protobuf `text` field) and an - * optional color used by the UI to tint slides / cues that carry the label. - * - * Labels do not have a UUID — identity is the name string. - */ class Label { public function __construct( @@ -20,30 +15,24 @@ class Label ) { } - /** - * Display name (the protobuf `text` field; this is what the - * ProPresenter UI renders next to the swatch). - */ public function getName(): string { return $this->label->getText(); } - /** - * Whether the label carries an explicit color message (the absence of a - * color is not the same as black: ProPresenter renders unset labels with - * the default UI color). - */ + public function setName(string $name): self + { + $this->label->setText($name); + + return $this; + } + public function hasColor(): bool { return $this->label->hasColor(); } /** - * Get the label's color as an associative array, or null when unset. - * - * Channels are floats in the 0..1 range as stored by ProPresenter. - * * @return array{r: float, g: float, b: float, a: float}|null */ public function getColor(): ?array @@ -62,10 +51,6 @@ class Label ]; } - /** - * Convenience: render the color as a 6-digit `#RRGGBB` hex string, - * dropping alpha. Returns null when the label has no color. - */ public function getColorHex(): ?string { $color = $this->getColor(); @@ -82,8 +67,48 @@ class Label } /** - * Get the underlying protobuf Label message. + * Set the label's color. Pass `null` to remove the color (the UI will + * fall back to its default tint). + * + * @param array{r: float, g: float, b: float, a?: float}|null $color */ + public function setColor(?array $color): self + { + if ($color === null) { + $this->label->clearColor(); + + return $this; + } + + $proto = new Color(); + $proto->setRed((float) $color['r']); + $proto->setGreen((float) $color['g']); + $proto->setBlue((float) $color['b']); + $proto->setAlpha((float) ($color['a'] ?? 1.0)); + $this->label->setColor($proto); + + return $this; + } + + /** + * Convenience setter: accepts a `#RRGGBB` or `#RRGGBBAA` hex value and + * applies it to the label. Alpha defaults to 1.0 when missing. + */ + public function setColorHex(string $hex): self + { + $hex = ltrim($hex, '#'); + if (!preg_match('/^[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/', $hex)) { + throw new InvalidArgumentException(sprintf('Invalid hex color: %s', $hex)); + } + + $r = hexdec(substr($hex, 0, 2)) / 255.0; + $g = hexdec(substr($hex, 2, 2)) / 255.0; + $b = hexdec(substr($hex, 4, 2)) / 255.0; + $a = strlen($hex) === 8 ? hexdec(substr($hex, 6, 2)) / 255.0 : 1.0; + + return $this->setColor(['r' => $r, 'g' => $g, 'b' => $b, 'a' => $a]); + } + public function getProto(): LabelProto { return $this->label; diff --git a/src/LabelLibrary.php b/src/LabelLibrary.php index f13c89a..ce6f4e1 100644 --- a/src/LabelLibrary.php +++ b/src/LabelLibrary.php @@ -4,17 +4,10 @@ declare(strict_types=1); namespace ProPresenter\Parser; +use Rv\Data\Action\Label as LabelProto; +use Rv\Data\Color; use Rv\Data\ProLabelsDocument; -/** - * Wraps the protobuf {@see ProLabelsDocument} — the global ProPresenter - * `Labels` file which lists every named label and its UI color. - * - * Labels are identified by name only (there is no UUID field). Names are - * expected to be unique inside the document; if the source file violates - * that, the first occurrence wins for {@see getLabelByName()} but every - * label is preserved in {@see getLabels()} in document order. - */ class LabelLibrary { /** @var Label[] */ @@ -30,16 +23,7 @@ class LabelLibrary private readonly ProLabelsDocument $document, ) { foreach ($this->document->getLabels() as $labelProto) { - $label = new Label($labelProto); - $this->labels[] = $label; - - $name = $label->getName(); - if ($name === '') { - continue; - } - - $this->labelsByName[$name] ??= $label; - $this->labelsByNameLower[strtolower($name)] ??= $label; + $this->register(new Label($labelProto)); } } @@ -56,28 +40,95 @@ class LabelLibrary return count($this->labels); } - /** - * Exact-name lookup. Use {@see findLabelByName()} for a - * case-insensitive variant. - */ public function getLabelByName(string $name): ?Label { return $this->labelsByName[$name] ?? null; } - /** - * Case-insensitive name lookup. - */ public function findLabelByName(string $name): ?Label { return $this->labelsByNameLower[strtolower($name)] ?? null; } /** - * Get the underlying protobuf ProLabelsDocument. + * Append a brand-new label to the document. + * + * @param array{r: float, g: float, b: float, a?: float}|null $color */ + public function addLabel(string $name, ?array $color = null): Label + { + $proto = new LabelProto(); + $proto->setText($name); + if ($color !== null) { + $colorProto = new Color(); + $colorProto->setRed((float) $color['r']); + $colorProto->setGreen((float) $color['g']); + $colorProto->setBlue((float) $color['b']); + $colorProto->setAlpha((float) ($color['a'] ?? 1.0)); + $proto->setColor($colorProto); + } + + $existing = iterator_to_array($this->document->getLabels()); + $existing[] = $proto; + $this->document->setLabels($existing); + + $label = new Label($proto); + $this->register($label); + + return $label; + } + + /** + * Remove a label by its current name. Returns true when something was + * removed. + */ + public function removeLabel(string $name): bool + { + $kept = []; + $removed = false; + foreach ($this->document->getLabels() as $proto) { + if (!$removed && $proto->getText() === $name) { + $removed = true; + continue; + } + $kept[] = $proto; + } + + if (!$removed) { + return false; + } + + $this->document->setLabels($kept); + $this->rebuildIndex(); + + return true; + } + public function getDocument(): ProLabelsDocument { return $this->document; } + + private function register(Label $label): void + { + $this->labels[] = $label; + + $name = $label->getName(); + if ($name === '') { + return; + } + + $this->labelsByName[$name] ??= $label; + $this->labelsByNameLower[strtolower($name)] ??= $label; + } + + private function rebuildIndex(): void + { + $this->labels = []; + $this->labelsByName = []; + $this->labelsByNameLower = []; + foreach ($this->document->getLabels() as $proto) { + $this->register(new Label($proto)); + } + } } diff --git a/src/LabelsFileWriter.php b/src/LabelsFileWriter.php new file mode 100644 index 0000000..90367a1 --- /dev/null +++ b/src/LabelsFileWriter.php @@ -0,0 +1,26 @@ +getDocument()->serializeToString(); + $writtenBytes = file_put_contents($filePath, $data); + + if ($writtenBytes === false) { + throw new RuntimeException(sprintf('Unable to write Labels file: %s', $filePath)); + } + } +} diff --git a/src/Macro.php b/src/Macro.php index cfc5182..e47bf93 100644 --- a/src/Macro.php +++ b/src/Macro.php @@ -4,16 +4,10 @@ declare(strict_types=1); namespace ProPresenter\Parser; +use Rv\Data\Color; use Rv\Data\MacrosDocument\Macro as MacroProto; +use Rv\Data\UUID; -/** - * Wraps a protobuf Macro, exposing its identifying fields (UUID, name) plus - * convenience metadata (color, action count, image type, startup flag). - * - * Macros live in the global ProPresenter `Macros` document and may belong to - * one or more {@see MacroCollection}s. Membership is resolved by - * {@see MacroLibrary}, not by this wrapper. - */ class Macro { public function __construct( @@ -21,25 +15,33 @@ class Macro ) { } - /** - * Get the macro's UUID as an upper-case string (empty when unset). - */ public function getUuid(): string { return $this->macro->getUuid()?->getString() ?? ''; } - /** - * Get the macro's display name. - */ + public function setUuid(string $uuid): self + { + $proto = new UUID(); + $proto->setString($uuid); + $this->macro->setUuid($proto); + + return $this; + } + public function getName(): string { return $this->macro->getName(); } + public function setName(string $name): self + { + $this->macro->setName($name); + + return $this; + } + /** - * Get the macro's color as an associative array, or null when unset. - * * @return array{r: float, g: float, b: float, a: float}|null */ public function getColor(): ?array @@ -59,35 +61,71 @@ class Macro } /** - * Whether the macro is configured to fire on application startup. + * @param array{r: float, g: float, b: float, a?: float}|null $color */ + public function setColor(?array $color): self + { + if ($color === null) { + $this->macro->clearColor(); + + return $this; + } + + $proto = new Color(); + $proto->setRed((float) $color['r']); + $proto->setGreen((float) $color['g']); + $proto->setBlue((float) $color['b']); + $proto->setAlpha((float) ($color['a'] ?? 1.0)); + $this->macro->setColor($proto); + + return $this; + } + public function getTriggerOnStartup(): bool { return $this->macro->getTriggerOnStartup(); } - /** - * Number of action entries attached to this macro. - * - * Action payloads are not exposed by this wrapper; use {@see getProto()} to - * inspect them via the generated protobuf classes. - */ + public function setTriggerOnStartup(bool $value): self + { + $this->macro->setTriggerOnStartup($value); + + return $this; + } + public function getActionCount(): int { return count($this->macro->getActions()); } - /** - * Icon enum value (see {@see \Rv\Data\MacrosDocument\Macro\ImageType}). - */ public function getImageType(): int { return $this->macro->getImageType(); } + public function setImageType(int $value): self + { + $this->macro->setImageType($value); + + return $this; + } + /** - * Get the underlying protobuf Macro message. + * Custom icon bytes (PNG/JPG). Empty when ProPresenter uses one of the + * built-in `image_type` icons. */ + public function getImageData(): string + { + return $this->macro->getImageData(); + } + + public function setImageData(string $bytes): self + { + $this->macro->setImageData($bytes); + + return $this; + } + public function getProto(): MacroProto { return $this->macro; diff --git a/src/MacroCollection.php b/src/MacroCollection.php index 6f8a42c..7fe1ba1 100644 --- a/src/MacroCollection.php +++ b/src/MacroCollection.php @@ -5,11 +5,9 @@ declare(strict_types=1); namespace ProPresenter\Parser; use Rv\Data\MacrosDocument\MacroCollection as MacroCollectionProto; +use Rv\Data\MacrosDocument\MacroCollection\Item as ItemProto; +use Rv\Data\UUID; -/** - * Wraps a protobuf MacroCollection. A collection groups macros by UUID - * reference; macro definitions themselves live at the document root. - */ class MacroCollection { public function __construct( @@ -17,28 +15,33 @@ class MacroCollection ) { } - /** - * Get the collection's UUID as a string (empty when unset). - */ public function getUuid(): string { return $this->collection->getUuid()?->getString() ?? ''; } - /** - * Get the collection's display name (e.g. "Ablauf"). - */ + public function setUuid(string $uuid): self + { + $proto = new UUID(); + $proto->setString($uuid); + $this->collection->setUuid($proto); + + return $this; + } + public function getName(): string { return $this->collection->getName(); } + public function setName(string $name): self + { + $this->collection->setName($name); + + return $this; + } + /** - * Get the UUIDs of macros referenced by this collection, in order. - * - * Items in the protobuf use a `oneof` ItemType — currently only - * `macro_id` is defined. Items without a populated reference are skipped. - * * @return string[] */ public function getMacroUuids(): array @@ -55,8 +58,39 @@ class MacroCollection } /** - * Get the underlying protobuf MacroCollection message. + * Replace the collection's referenced macro UUIDs in one call. Pass UUID + * strings exactly as ProPresenter writes them (upper-case is conventional). + * + * @param string[] $uuids */ + public function setMacroUuids(array $uuids): self + { + $items = []; + foreach ($uuids as $uuid) { + $item = new ItemProto(); + $ref = new UUID(); + $ref->setString($uuid); + $item->setMacroId($ref); + $items[] = $item; + } + $this->collection->setItems($items); + + return $this; + } + + public function addMacroUuid(string $uuid): self + { + $items = iterator_to_array($this->collection->getItems()); + $item = new ItemProto(); + $ref = new UUID(); + $ref->setString($uuid); + $item->setMacroId($ref); + $items[] = $item; + $this->collection->setItems($items); + + return $this; + } + public function getProto(): MacroCollectionProto { return $this->collection; diff --git a/src/MacroLibrary.php b/src/MacroLibrary.php index e9372d6..0d927d2 100644 --- a/src/MacroLibrary.php +++ b/src/MacroLibrary.php @@ -5,16 +5,10 @@ declare(strict_types=1); namespace ProPresenter\Parser; use Rv\Data\MacrosDocument; +use Rv\Data\MacrosDocument\Macro as MacroProto; +use Rv\Data\MacrosDocument\MacroCollection as MacroCollectionProto; +use Rv\Data\UUID; -/** - * Wraps a protobuf MacrosDocument — the global ProPresenter `Macros` file - * which lists every macro definition and the collections that group them. - * - * Lookup helpers index macros and collections by UUID (case-insensitive) and - * by name, mirroring the convention used by {@see Song}. Collection - * membership is resolved by mapping {@see MacroCollection::getMacroUuids()} - * back through this library. - */ class MacroLibrary { /** @var Macro[] */ @@ -41,43 +35,7 @@ class MacroLibrary public function __construct( private readonly MacrosDocument $document, ) { - foreach ($this->document->getMacros() as $macroProto) { - $macro = new Macro($macroProto); - $this->macros[] = $macro; - - $uuid = strtoupper($macro->getUuid()); - if ($uuid !== '') { - $this->macrosByUuid[$uuid] = $macro; - } - - $name = $macro->getName(); - if ($name !== '') { - $this->macrosByName[$name] = $macro; - } - } - - foreach ($this->document->getMacroCollections() as $collectionProto) { - $collection = new MacroCollection($collectionProto); - $this->collections[] = $collection; - - $uuid = strtoupper($collection->getUuid()); - if ($uuid !== '') { - $this->collectionsByUuid[$uuid] = $collection; - } - - $name = $collection->getName(); - if ($name !== '') { - $this->collectionsByName[$name] = $collection; - } - - foreach ($collection->getMacroUuids() as $macroUuid) { - $key = strtoupper($macroUuid); - if ($key === '') { - continue; - } - $this->collectionsByMacroUuid[$key][] = $collection; - } - } + $this->rebuildIndex(); } /** @@ -117,10 +75,6 @@ class MacroLibrary } /** - * Resolve a collection's referenced macros to {@see Macro} wrappers. - * - * Unknown UUIDs (referenced but not defined at document root) are skipped. - * * @return Macro[] */ public function getMacrosForCollection(MacroCollection $collection): array @@ -137,10 +91,6 @@ class MacroLibrary } /** - * Get the collections a macro belongs to (membership is by UUID - * reference). A macro may legally appear in zero, one, or many - * collections. - * * @return MacroCollection[] */ public function getCollectionsForMacro(Macro $macro): array @@ -154,10 +104,141 @@ class MacroLibrary } /** - * Get the underlying protobuf MacrosDocument. + * Append a brand-new macro to the document. */ + public function addMacro(string $name, string $uuid): Macro + { + $proto = new MacroProto(); + $uuidProto = new UUID(); + $uuidProto->setString($uuid); + $proto->setUuid($uuidProto); + $proto->setName($name); + + $existing = iterator_to_array($this->document->getMacros()); + $existing[] = $proto; + $this->document->setMacros($existing); + + $this->rebuildIndex(); + + return $this->getMacroByUuid($uuid) ?? new Macro($proto); + } + + public function removeMacro(string $uuid): bool + { + $needle = strtoupper($uuid); + $kept = []; + $removed = false; + foreach ($this->document->getMacros() as $proto) { + $current = strtoupper($proto->getUuid()?->getString() ?? ''); + if (!$removed && $current === $needle) { + $removed = true; + continue; + } + $kept[] = $proto; + } + + if (!$removed) { + return false; + } + + $this->document->setMacros($kept); + $this->rebuildIndex(); + + return true; + } + + public function addCollection(string $name, string $uuid): MacroCollection + { + $proto = new MacroCollectionProto(); + $uuidProto = new UUID(); + $uuidProto->setString($uuid); + $proto->setUuid($uuidProto); + $proto->setName($name); + + $existing = iterator_to_array($this->document->getMacroCollections()); + $existing[] = $proto; + $this->document->setMacroCollections($existing); + + $this->rebuildIndex(); + + return $this->getCollectionByUuid($uuid) ?? new MacroCollection($proto); + } + + public function removeCollection(string $uuid): bool + { + $needle = strtoupper($uuid); + $kept = []; + $removed = false; + foreach ($this->document->getMacroCollections() as $proto) { + $current = strtoupper($proto->getUuid()?->getString() ?? ''); + if (!$removed && $current === $needle) { + $removed = true; + continue; + } + $kept[] = $proto; + } + + if (!$removed) { + return false; + } + + $this->document->setMacroCollections($kept); + $this->rebuildIndex(); + + return true; + } + public function getDocument(): MacrosDocument { return $this->document; } + + private function rebuildIndex(): void + { + $this->macros = []; + $this->collections = []; + $this->macrosByUuid = []; + $this->macrosByName = []; + $this->collectionsByUuid = []; + $this->collectionsByName = []; + $this->collectionsByMacroUuid = []; + + foreach ($this->document->getMacros() as $macroProto) { + $macro = new Macro($macroProto); + $this->macros[] = $macro; + + $uuid = strtoupper($macro->getUuid()); + if ($uuid !== '') { + $this->macrosByUuid[$uuid] = $macro; + } + + $name = $macro->getName(); + if ($name !== '') { + $this->macrosByName[$name] = $macro; + } + } + + foreach ($this->document->getMacroCollections() as $collectionProto) { + $collection = new MacroCollection($collectionProto); + $this->collections[] = $collection; + + $uuid = strtoupper($collection->getUuid()); + if ($uuid !== '') { + $this->collectionsByUuid[$uuid] = $collection; + } + + $name = $collection->getName(); + if ($name !== '') { + $this->collectionsByName[$name] = $collection; + } + + foreach ($collection->getMacroUuids() as $macroUuid) { + $key = strtoupper($macroUuid); + if ($key === '') { + continue; + } + $this->collectionsByMacroUuid[$key][] = $collection; + } + } + } } diff --git a/src/MacrosFileWriter.php b/src/MacrosFileWriter.php new file mode 100644 index 0000000..d56758d --- /dev/null +++ b/src/MacrosFileWriter.php @@ -0,0 +1,26 @@ +getDocument()->serializeToString(); + $writtenBytes = file_put_contents($filePath, $data); + + if ($writtenBytes === false) { + throw new RuntimeException(sprintf('Unable to write Macros file: %s', $filePath)); + } + } +} diff --git a/src/Message.php b/src/Message.php new file mode 100644 index 0000000..92e32e2 --- /dev/null +++ b/src/Message.php @@ -0,0 +1,103 @@ +message->getUuid()?->getString() ?? ''; + } + + public function setUuid(string $uuid): self + { + $proto = new UUID(); + $proto->setString($uuid); + $this->message->setUuid($proto); + + return $this; + } + + public function getTitle(): string + { + return $this->message->getTitle(); + } + + public function setTitle(string $title): self + { + $this->message->setTitle($title); + + return $this; + } + + public function getTimeToRemove(): float + { + return $this->message->getTimeToRemove(); + } + + public function setTimeToRemove(float $timeToRemove): self + { + $this->message->setTimeToRemove($timeToRemove); + + return $this; + } + + public function isVisibleOnNetwork(): bool + { + return $this->message->getVisibleOnNetwork(); + } + + public function setVisibleOnNetwork(bool $visibleOnNetwork): self + { + $this->message->setVisibleOnNetwork($visibleOnNetwork); + + return $this; + } + + public function getMessageText(): string + { + return $this->message->getMessageText(); + } + + public function setMessageText(string $messageText): self + { + $this->message->setMessageText($messageText); + + return $this; + } + + public function getClearType(): int + { + return $this->message->getClearType(); + } + + public function setClearType(int $clearType): self + { + $this->message->setClearType($clearType); + + return $this; + } + + /** + * @return mixed raw repeated \Rv\Data\Message\Token protos + */ + public function getTokens(): mixed + { + return $this->message->getTokens(); + } + + public function getProto(): MessageProto + { + return $this->message; + } +} diff --git a/src/MessageLibrary.php b/src/MessageLibrary.php new file mode 100644 index 0000000..e7f8b3e --- /dev/null +++ b/src/MessageLibrary.php @@ -0,0 +1,123 @@ + */ + private array $messagesByUuid = []; + + /** @var array */ + private array $messagesByName = []; + + public function __construct( + private readonly MessageDocument $document, + ) { + $this->rebuildIndex(); + } + + /** + * @return Message[] + */ + public function getMessages(): array + { + return $this->messages; + } + + public function count(): int + { + return count($this->messages); + } + + public function getMessageByUuid(string $uuid): ?Message + { + return $this->messagesByUuid[strtoupper($uuid)] ?? null; + } + + public function getMessageByName(string $name): ?Message + { + return $this->messagesByName[$name] ?? null; + } + + public function addMessage(string $title, string $uuid): Message + { + $proto = new MessageProto(); + $uuidProto = new UUID(); + $uuidProto->setString($uuid); + $proto->setUuid($uuidProto); + $proto->setTitle($title); + + $existing = iterator_to_array($this->document->getMessages()); + $existing[] = $proto; + $this->document->setMessages($existing); + $this->rebuildIndex(); + + return $this->getMessageByUuid($uuid) ?? new Message($proto); + } + + public function removeMessage(string $uuid): bool + { + $needle = strtoupper($uuid); + $kept = []; + $removed = false; + foreach ($this->document->getMessages() as $proto) { + $current = strtoupper($proto->getUuid()?->getString() ?? ''); + if (!$removed && $current === $needle) { + $removed = true; + continue; + } + $kept[] = $proto; + } + + if (!$removed) { + return false; + } + + $this->document->setMessages($kept); + $this->rebuildIndex(); + + return true; + } + + public function getApplicationInfo(): ?ApplicationInfo + { + return $this->document->getApplicationInfo(); + } + + public function getDocument(): MessageDocument + { + return $this->document; + } + + private function rebuildIndex(): void + { + $this->messages = []; + $this->messagesByUuid = []; + $this->messagesByName = []; + + foreach ($this->document->getMessages() as $proto) { + $message = new Message($proto); + $this->messages[] = $message; + + $uuid = strtoupper($message->getUuid()); + if ($uuid !== '') { + $this->messagesByUuid[$uuid] = $message; + } + + $title = $message->getTitle(); + if ($title !== '') { + $this->messagesByName[$title] ??= $message; + } + } + } +} diff --git a/src/MessagesFileReader.php b/src/MessagesFileReader.php new file mode 100644 index 0000000..c5524f1 --- /dev/null +++ b/src/MessagesFileReader.php @@ -0,0 +1,38 @@ +mergeFromString($data); + + return new MessageLibrary($document); + } +} diff --git a/src/MessagesFileWriter.php b/src/MessagesFileWriter.php new file mode 100644 index 0000000..b664826 --- /dev/null +++ b/src/MessagesFileWriter.php @@ -0,0 +1,24 @@ +getDocument()->serializeToString()); + if ($writtenBytes === false) { + throw new RuntimeException(sprintf('Unable to write Messages file: %s', $filePath)); + } + } +} diff --git a/src/Prop.php b/src/Prop.php new file mode 100644 index 0000000..f23fd5c --- /dev/null +++ b/src/Prop.php @@ -0,0 +1,77 @@ +cue->getUuid()?->getString() ?? ''; + } + + public function setUuid(string $uuid): self + { + $proto = new UUID(); + $proto->setString($uuid); + $this->cue->setUuid($proto); + + return $this; + } + + public function getName(): string + { + return $this->cue->getName(); + } + + public function setName(string $name): self + { + $this->cue->setName($name); + + return $this; + } + + public function isEnabled(): bool + { + return $this->cue->getIsEnabled(); + } + + public function setEnabled(bool $enabled): self + { + $this->cue->setIsEnabled($enabled); + + return $this; + } + + public function getCompletionTime(): float + { + return $this->cue->getCompletionTime(); + } + + public function setCompletionTime(float $completionTime): self + { + $this->cue->setCompletionTime($completionTime); + + return $this; + } + + public function getActions(): RepeatedField + { + return $this->cue->getActions(); + } + + public function getProto(): Cue + { + return $this->cue; + } +} diff --git a/src/PropLibrary.php b/src/PropLibrary.php new file mode 100644 index 0000000..3f4dc45 --- /dev/null +++ b/src/PropLibrary.php @@ -0,0 +1,115 @@ + */ + private array $propsByUuid = []; + + /** @var array */ + private array $propsByName = []; + + public function __construct( + private readonly PropDocument $document, + ) { + $this->rebuildIndex(); + } + + public function getDocument(): PropDocument + { + return $this->document; + } + + /** @return Prop[] */ + public function getProps(): array + { + return $this->props; + } + + public function getPropByUuid(string $uuid): ?Prop + { + return $this->propsByUuid[strtoupper($uuid)] ?? null; + } + + public function getPropByName(string $name): ?Prop + { + return $this->propsByName[$name] ?? null; + } + + public function addProp(Prop|Cue $prop): Prop + { + $proto = $prop instanceof Prop ? $prop->getProto() : $prop; + $existing = iterator_to_array($this->document->getCues()); + $existing[] = $proto; + $this->document->setCues($existing); + $this->rebuildIndex(); + + return $this->props[array_key_last($this->props)]; + } + + public function removeProp(string $uuid): bool + { + $needle = strtoupper($uuid); + $kept = []; + $removed = false; + foreach ($this->document->getCues() as $proto) { + $current = strtoupper($proto->getUuid()?->getString() ?? ''); + if (!$removed && $current === $needle) { + $removed = true; + continue; + } + $kept[] = $proto; + } + + if (!$removed) { + return false; + } + + $this->document->setCues($kept); + $this->rebuildIndex(); + + return true; + } + + public function count(): int + { + return count($this->props); + } + + public function getApplicationInfo(): ?ApplicationInfo + { + return $this->document->getApplicationInfo(); + } + + private function rebuildIndex(): void + { + $this->props = []; + $this->propsByUuid = []; + $this->propsByName = []; + + foreach ($this->document->getCues() as $proto) { + $prop = new Prop($proto); + $this->props[] = $prop; + + $uuid = strtoupper($prop->getUuid()); + if ($uuid !== '') { + $this->propsByUuid[$uuid] = $prop; + } + + $name = $prop->getName(); + if ($name !== '') { + $this->propsByName[$name] ??= $prop; + } + } + } +} diff --git a/src/PropsFileReader.php b/src/PropsFileReader.php new file mode 100644 index 0000000..1e6a62a --- /dev/null +++ b/src/PropsFileReader.php @@ -0,0 +1,35 @@ +mergeFromString($data); + + return new PropLibrary($document); + } +} diff --git a/src/PropsFileWriter.php b/src/PropsFileWriter.php new file mode 100644 index 0000000..c6b180a --- /dev/null +++ b/src/PropsFileWriter.php @@ -0,0 +1,22 @@ +getDocument()->serializeToString()) === false) { + throw new RuntimeException(sprintf('Unable to write Props file: %s', $filePath)); + } + } +} diff --git a/src/Screen.php b/src/Screen.php new file mode 100644 index 0000000..37d6bc8 --- /dev/null +++ b/src/Screen.php @@ -0,0 +1,71 @@ +screen->getName(); + } + + public function setName(string $name): self + { + $this->screen->setName($name); + + return $this; + } + + public function getUuid(): string + { + return $this->screen->getUuid()?->getString() ?? ''; + } + + public function setUuid(string $uuid): self + { + $proto = new UUID(); + $proto->setString($uuid); + $this->screen->setUuid($proto); + + return $this; + } + + public function getScreenType(): int + { + return $this->screen->getScreenType(); + } + + public function setScreenType(int $screenType): self + { + $this->screen->setScreenType($screenType); + + return $this; + } + + public function getIndex(): ?int + { + if ($this->screen->hasArrangementSingle()) { + $arrangement = $this->screen->getArrangementSingle(); + if ($arrangement !== null && count($arrangement->getScreens()) > 0) { + return 0; + } + } + + return null; + } + + public function getProto(): ProPresenterScreen + { + return $this->screen; + } +} diff --git a/src/StageFileReader.php b/src/StageFileReader.php new file mode 100644 index 0000000..25c447e --- /dev/null +++ b/src/StageFileReader.php @@ -0,0 +1,38 @@ +mergeFromString($data); + + return new StageLibrary($document); + } +} diff --git a/src/StageFileWriter.php b/src/StageFileWriter.php new file mode 100644 index 0000000..7597927 --- /dev/null +++ b/src/StageFileWriter.php @@ -0,0 +1,24 @@ +getDocument()->serializeToString()); + if ($writtenBytes === false) { + throw new RuntimeException(sprintf('Unable to write Stage file: %s', $filePath)); + } + } +} diff --git a/src/StageLayout.php b/src/StageLayout.php new file mode 100644 index 0000000..59d6dce --- /dev/null +++ b/src/StageLayout.php @@ -0,0 +1,53 @@ +layout->getUuid()?->getString() ?? ''; + } + + public function setUuid(string $uuid): self + { + $proto = new UUID(); + $proto->setString($uuid); + $this->layout->setUuid($proto); + + return $this; + } + + public function getName(): string + { + return $this->layout->getName(); + } + + public function setName(string $name): self + { + $this->layout->setName($name); + + return $this; + } + + public function getSlide(): ?Slide + { + return $this->layout->getSlide(); + } + + public function getProto(): LayoutProto + { + return $this->layout; + } +} diff --git a/src/StageLibrary.php b/src/StageLibrary.php new file mode 100644 index 0000000..fc722d6 --- /dev/null +++ b/src/StageLibrary.php @@ -0,0 +1,115 @@ + */ + private array $layoutsByUuid = []; + + /** @var array */ + private array $layoutsByName = []; + + public function __construct( + private readonly Document $document, + ) { + $this->rebuildIndex(); + } + + public function getDocument(): Document + { + return $this->document; + } + + /** @return StageLayout[] */ + public function getLayouts(): array + { + return $this->layouts; + } + + public function getLayoutByUuid(string $uuid): ?StageLayout + { + return $this->layoutsByUuid[strtoupper($uuid)] ?? null; + } + + public function getLayoutByName(string $name): ?StageLayout + { + return $this->layoutsByName[$name] ?? null; + } + + public function addLayout(StageLayout|LayoutProto $layout): StageLayout + { + $proto = $layout instanceof StageLayout ? $layout->getProto() : $layout; + $existing = iterator_to_array($this->document->getLayouts()); + $existing[] = $proto; + $this->document->setLayouts($existing); + $this->rebuildIndex(); + + return $this->layouts[array_key_last($this->layouts)]; + } + + public function removeLayout(string $uuid): bool + { + $needle = strtoupper($uuid); + $kept = []; + $removed = false; + foreach ($this->document->getLayouts() as $proto) { + $current = strtoupper($proto->getUuid()?->getString() ?? ''); + if (!$removed && $current === $needle) { + $removed = true; + continue; + } + $kept[] = $proto; + } + + if (!$removed) { + return false; + } + + $this->document->setLayouts($kept); + $this->rebuildIndex(); + + return true; + } + + public function count(): int + { + return count($this->layouts); + } + + public function getApplicationInfo(): ?ApplicationInfo + { + return $this->document->getApplicationInfo(); + } + + private function rebuildIndex(): void + { + $this->layouts = []; + $this->layoutsByUuid = []; + $this->layoutsByName = []; + + foreach ($this->document->getLayouts() as $proto) { + $layout = new StageLayout($proto); + $this->layouts[] = $layout; + + $uuid = strtoupper($layout->getUuid()); + if ($uuid !== '') { + $this->layoutsByUuid[$uuid] = $layout; + } + + $name = $layout->getName(); + if ($name !== '') { + $this->layoutsByName[$name] ??= $layout; + } + } + } +} diff --git a/src/TestPatternsFileReader.php b/src/TestPatternsFileReader.php new file mode 100644 index 0000000..53fb0a5 --- /dev/null +++ b/src/TestPatternsFileReader.php @@ -0,0 +1,38 @@ +mergeFromString($data); + + return new TestPatternsLibrary($document); + } +} diff --git a/src/TestPatternsFileWriter.php b/src/TestPatternsFileWriter.php new file mode 100644 index 0000000..1d846a1 --- /dev/null +++ b/src/TestPatternsFileWriter.php @@ -0,0 +1,26 @@ +getDocument()->serializeToString(); + $writtenBytes = file_put_contents($filePath, $data); + + if ($writtenBytes === false) { + throw new RuntimeException(sprintf('Unable to write TestPatterns file: %s', $filePath)); + } + } +} diff --git a/src/TestPatternsLibrary.php b/src/TestPatternsLibrary.php new file mode 100644 index 0000000..33a4ffe --- /dev/null +++ b/src/TestPatternsLibrary.php @@ -0,0 +1,161 @@ + */ + private array $patternsByUuid = []; + + /** @var array */ + private array $patternsByName = []; + + public function __construct( + private readonly TestPatternDocument $document, + ) { + $this->rebuildIndex(); + } + + public function getState(): ?TestPatternStateData + { + if (!$this->document->hasState()) { + return null; + } + + return $this->document->getState(); + } + + public function setState(?TestPatternStateData $state): self + { + if ($state === null) { + $this->document->clearState(); + + return $this; + } + + $this->document->setState($state); + + return $this; + } + + public function getSelectedPatternUuid(): string + { + return $this->getState()?->getTestPatternId()?->getString() ?? ''; + } + + public function getSelectedPatternNameLocalizationKey(): string + { + return $this->getState()?->getTestPatternNameLocalizationKey() ?? ''; + } + + public function getDisplayLocation(): int + { + return $this->getState()?->getDisplayLocation() ?? 0; + } + + public function getSpecificScreenUuid(): string + { + return $this->getState()?->getSpecificScreen()?->getString() ?? ''; + } + + /** + * Return saved test pattern definitions in document order. + * + * @return TestPatternData[] + */ + public function getPatterns(): array + { + return $this->patterns; + } + + public function count(): int + { + return count($this->patterns); + } + + public function getPatternByUuid(string $uuid): ?TestPatternData + { + return $this->patternsByUuid[strtoupper($uuid)] ?? null; + } + + public function getPatternByName(string $nameLocalizationKey): ?TestPatternData + { + return $this->patternsByName[$nameLocalizationKey] ?? null; + } + + public function addPattern(string $nameLocalizationKey, string $uuid): TestPatternData + { + $proto = new TestPatternData(); + $uuidProto = new UUID(); + $uuidProto->setString($uuid); + $proto->setUuid($uuidProto); + $proto->setNameLocalizationKey($nameLocalizationKey); + + $existing = iterator_to_array($this->document->getPatterns()); + $existing[] = $proto; + $this->document->setPatterns($existing); + $this->rebuildIndex(); + + return $this->getPatternByUuid($uuid) ?? $proto; + } + + public function removePattern(string $uuid): bool + { + $needle = strtoupper($uuid); + $kept = []; + $removed = false; + foreach ($this->document->getPatterns() as $proto) { + $current = strtoupper($proto->getUuid()?->getString() ?? ''); + if (!$removed && $current === $needle) { + $removed = true; + continue; + } + $kept[] = $proto; + } + + if (!$removed) { + return false; + } + + $this->document->setPatterns($kept); + $this->rebuildIndex(); + + return true; + } + + public function getDocument(): TestPatternDocument + { + return $this->document; + } + + private function rebuildIndex(): void + { + $this->patterns = []; + $this->patternsByUuid = []; + $this->patternsByName = []; + + foreach ($this->document->getPatterns() as $proto) { + $this->patterns[] = $proto; + + $uuid = strtoupper($proto->getUuid()?->getString() ?? ''); + if ($uuid !== '') { + $this->patternsByUuid[$uuid] = $proto; + } + + $name = $proto->getNameLocalizationKey(); + if ($name !== '') { + $this->patternsByName[$name] ??= $proto; + } + } + } +} diff --git a/src/ThemeAsset.php b/src/ThemeAsset.php new file mode 100644 index 0000000..e7af96c --- /dev/null +++ b/src/ThemeAsset.php @@ -0,0 +1,45 @@ +name; + } + + public function getBytes(): string + { + return $this->bytes; + } + + public function getSize(): int + { + return strlen($this->bytes); + } + + public function getMimeType(): string + { + return match (strtolower(pathinfo($this->name, PATHINFO_EXTENSION))) { + 'jpg', 'jpeg' => 'image/jpeg', + 'png' => 'image/png', + 'gif' => 'image/gif', + 'webp' => 'image/webp', + 'svg' => 'image/svg+xml', + 'mp4' => 'video/mp4', + 'mov' => 'video/quicktime', + 'mp3' => 'audio/mpeg', + 'wav' => 'audio/wav', + default => 'application/octet-stream', + }; + } +} diff --git a/src/ThemeBundle.php b/src/ThemeBundle.php new file mode 100644 index 0000000..d95b9c0 --- /dev/null +++ b/src/ThemeBundle.php @@ -0,0 +1,135 @@ + */ + private array $slidesByName = []; + + /** @var array */ + private array $assetsByName = []; + + /** + * @param ThemeAsset[] $assets + */ + public function __construct( + private readonly Document $document, + array $assets = [], + ) { + foreach ($assets as $asset) { + $this->assetsByName[$asset->getName()] = $asset; + } + $this->rebuildSlideIndex(); + } + + public function getDocument(): Document + { + return $this->document; + } + + /** @return ThemeSlide[] */ + public function getSlides(): array + { + return $this->slides; + } + + public function getSlideByName(string $name): ?ThemeSlide + { + return $this->slidesByName[$name] ?? null; + } + + public function addSlide(ThemeSlide|TemplateSlide $slide): ThemeSlide + { + $proto = $slide instanceof ThemeSlide ? $slide->getProto() : $slide; + $existing = iterator_to_array($this->document->getSlides()); + $existing[] = $proto; + $this->document->setSlides($existing); + $this->rebuildSlideIndex(); + + return $this->slides[array_key_last($this->slides)]; + } + + public function removeSlide(string $name): bool + { + $kept = []; + $removed = false; + foreach ($this->document->getSlides() as $proto) { + if (!$removed && $proto->getName() === $name) { + $removed = true; + continue; + } + $kept[] = $proto; + } + + if (!$removed) { + return false; + } + + $this->document->setSlides($kept); + $this->rebuildSlideIndex(); + + return true; + } + + /** @return ThemeAsset[] */ + public function getAssets(): array + { + return array_values($this->assetsByName); + } + + public function getAssetByName(string $name): ?ThemeAsset + { + return $this->assetsByName[$name] ?? null; + } + + public function addAsset(string $name, string $bytes): ThemeAsset + { + $asset = new ThemeAsset(basename($name), $bytes); + $this->assetsByName[$asset->getName()] = $asset; + + return $asset; + } + + public function removeAsset(string $name): bool + { + if (!isset($this->assetsByName[$name])) { + return false; + } + + unset($this->assetsByName[$name]); + + return true; + } + + public function count(): int + { + return count($this->slides); + } + + public function getAssetCount(): int + { + return count($this->assetsByName); + } + + private function rebuildSlideIndex(): void + { + $this->slides = []; + $this->slidesByName = []; + foreach ($this->document->getSlides() as $proto) { + $slide = new ThemeSlide($proto); + $this->slides[] = $slide; + if ($slide->getName() !== '') { + $this->slidesByName[$slide->getName()] ??= $slide; + } + } + } +} diff --git a/src/ThemeFileReader.php b/src/ThemeFileReader.php new file mode 100644 index 0000000..01654de --- /dev/null +++ b/src/ThemeFileReader.php @@ -0,0 +1,68 @@ +mergeFromString($data); + + return new ThemeBundle($document, self::readAssets($folderPath)); + } + + /** @return ThemeAsset[] */ + private static function readAssets(string $folderPath): array + { + $assetsPath = rtrim($folderPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'Assets'; + if (!is_dir($assetsPath)) { + return []; + } + + $entries = scandir($assetsPath); + if ($entries === false) { + throw new RuntimeException(sprintf('Unable to scan Assets directory: %s', $assetsPath)); + } + + $assets = []; + foreach ($entries as $entry) { + if ($entry === '.' || $entry === '..') { + continue; + } + $path = $assetsPath . DIRECTORY_SEPARATOR . $entry; + if (!is_file($path)) { + continue; + } + $bytes = file_get_contents($path); + if ($bytes === false) { + throw new RuntimeException(sprintf('Unable to read Theme asset: %s', $path)); + } + $assets[] = new ThemeAsset($entry, $bytes); + } + + usort($assets, static fn (ThemeAsset $a, ThemeAsset $b): int => strcmp($a->getName(), $b->getName())); + + return $assets; + } +} diff --git a/src/ThemeFileWriter.php b/src/ThemeFileWriter.php new file mode 100644 index 0000000..49d72ee --- /dev/null +++ b/src/ThemeFileWriter.php @@ -0,0 +1,51 @@ +getDocument()->serializeToString()) === false) { + throw new RuntimeException(sprintf('Unable to write Theme file: %s', $themePath)); + } + + $assetsPath = rtrim($folderPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'Assets'; + if (!is_dir($assetsPath) && !mkdir($assetsPath, 0777, true)) { + throw new RuntimeException(sprintf('Unable to create Assets directory: %s', $assetsPath)); + } + + $expected = []; + foreach ($bundle->getAssets() as $asset) { + $name = basename($asset->getName()); + $expected[$name] = true; + $path = $assetsPath . DIRECTORY_SEPARATOR . $name; + if (file_put_contents($path, $asset->getBytes()) === false) { + throw new RuntimeException(sprintf('Unable to write Theme asset: %s', $path)); + } + } + + $entries = scandir($assetsPath); + if ($entries === false) { + throw new RuntimeException(sprintf('Unable to scan Assets directory: %s', $assetsPath)); + } + foreach ($entries as $entry) { + if ($entry === '.' || $entry === '..' || isset($expected[$entry])) { + continue; + } + $path = $assetsPath . DIRECTORY_SEPARATOR . $entry; + if (is_file($path) && !unlink($path)) { + throw new RuntimeException(sprintf('Unable to remove stale Theme asset: %s', $path)); + } + } + } +} diff --git a/src/ThemeSlide.php b/src/ThemeSlide.php new file mode 100644 index 0000000..e65b318 --- /dev/null +++ b/src/ThemeSlide.php @@ -0,0 +1,38 @@ +slide->getName(); + } + + public function setName(string $name): self + { + $this->slide->setName($name); + + return $this; + } + + public function getBaseSlide(): ?BaseSlide + { + return $this->slide->getBaseSlide(); + } + + public function getProto(): TemplateSlide + { + return $this->slide; + } +} diff --git a/src/Timer.php b/src/Timer.php new file mode 100644 index 0000000..603e919 --- /dev/null +++ b/src/Timer.php @@ -0,0 +1,78 @@ +timer->getUuid()?->getString() ?? ''; + } + + public function setUuid(string $uuid): self + { + $proto = new UUID(); + $proto->setString($uuid); + $this->timer->setUuid($proto); + + return $this; + } + + public function getName(): string + { + return $this->timer->getName(); + } + + public function setName(string $name): self + { + $this->timer->setName($name); + + return $this; + } + + public function getConfiguration(): ?Configuration + { + return $this->timer->getConfiguration(); + } + + public function isCountdown(): bool + { + return $this->timer->getConfiguration()?->hasCountdown() ?? false; + } + + public function isCountdownToTime(): bool + { + return $this->timer->getConfiguration()?->hasCountdownToTime() ?? false; + } + + public function isElapsedTime(): bool + { + return $this->timer->getConfiguration()?->hasElapsedTime() ?? false; + } + + public function getDurationSeconds(): ?int + { + $countdown = $this->timer->getConfiguration()?->getCountdown(); + if ($countdown === null) { + return null; + } + + return (int) round($countdown->getDuration()); + } + + public function getProto(): TimerProto + { + return $this->timer; + } +} diff --git a/src/TimersFileReader.php b/src/TimersFileReader.php new file mode 100644 index 0000000..fc05f76 --- /dev/null +++ b/src/TimersFileReader.php @@ -0,0 +1,37 @@ +mergeFromString($data); + + return new TimersLibrary($document); + } +} diff --git a/src/TimersFileWriter.php b/src/TimersFileWriter.php new file mode 100644 index 0000000..cdadc7e --- /dev/null +++ b/src/TimersFileWriter.php @@ -0,0 +1,24 @@ +getDocument()->serializeToString()); + if ($writtenBytes === false) { + throw new RuntimeException(sprintf('Unable to write Timers file: %s', $filePath)); + } + } +} diff --git a/src/TimersLibrary.php b/src/TimersLibrary.php new file mode 100644 index 0000000..8594b0e --- /dev/null +++ b/src/TimersLibrary.php @@ -0,0 +1,136 @@ + */ + private array $timersByUuid = []; + + /** @var array */ + private array $timersByName = []; + + public function __construct( + private readonly TimersDocument $document, + ) { + $this->rebuildIndex(); + } + + /** + * @return Timer[] + */ + public function getTimers(): array + { + return $this->timers; + } + + public function count(): int + { + return count($this->timers); + } + + public function getTimerByUuid(string $uuid): ?Timer + { + return $this->timersByUuid[strtoupper($uuid)] ?? null; + } + + public function getTimerByName(string $name): ?Timer + { + return $this->timersByName[$name] ?? null; + } + + public function addTimer(string $name, string $uuid): Timer + { + $proto = new TimerProto(); + $uuidProto = new UUID(); + $uuidProto->setString($uuid); + $proto->setUuid($uuidProto); + $proto->setName($name); + + $existing = iterator_to_array($this->document->getTimers()); + $existing[] = $proto; + $this->document->setTimers($existing); + $this->rebuildIndex(); + + return $this->getTimerByUuid($uuid) ?? new Timer($proto); + } + + public function removeTimer(string $uuid): bool + { + $needle = strtoupper($uuid); + $kept = []; + $removed = false; + foreach ($this->document->getTimers() as $proto) { + $current = strtoupper($proto->getUuid()?->getString() ?? ''); + if (!$removed && $current === $needle) { + $removed = true; + continue; + } + $kept[] = $proto; + } + + if (!$removed) { + return false; + } + + $this->document->setTimers($kept); + $this->rebuildIndex(); + + return true; + } + + public function getClockFormat(): string + { + return $this->document->getClock()?->getFormat() ?? ''; + } + + public function setClockFormat(string $format): void + { + $clock = $this->document->getClock() ?? new Clock(); + $clock->setFormat($format); + $this->document->setClock($clock); + } + + public function getApplicationInfo(): ?ApplicationInfo + { + return $this->document->getApplicationInfo(); + } + + public function getDocument(): TimersDocument + { + return $this->document; + } + + private function rebuildIndex(): void + { + $this->timers = []; + $this->timersByUuid = []; + $this->timersByName = []; + + foreach ($this->document->getTimers() as $proto) { + $timer = new Timer($proto); + $this->timers[] = $timer; + + $uuid = strtoupper($timer->getUuid()); + if ($uuid !== '') { + $this->timersByUuid[$uuid] = $timer; + } + + $name = $timer->getName(); + if ($name !== '') { + $this->timersByName[$name] ??= $timer; + } + } + } +} diff --git a/src/WorkspaceFileReader.php b/src/WorkspaceFileReader.php new file mode 100644 index 0000000..971e837 --- /dev/null +++ b/src/WorkspaceFileReader.php @@ -0,0 +1,35 @@ +mergeFromString($data); + + return new WorkspaceLibrary($document); + } +} diff --git a/src/WorkspaceFileWriter.php b/src/WorkspaceFileWriter.php new file mode 100644 index 0000000..4973edf --- /dev/null +++ b/src/WorkspaceFileWriter.php @@ -0,0 +1,22 @@ +getDocument()->serializeToString()) === false) { + throw new RuntimeException(sprintf('Unable to write Workspace file: %s', $filePath)); + } + } +} diff --git a/src/WorkspaceLibrary.php b/src/WorkspaceLibrary.php new file mode 100644 index 0000000..3071481 --- /dev/null +++ b/src/WorkspaceLibrary.php @@ -0,0 +1,137 @@ + */ + private array $screensByUuid = []; + + /** @var array */ + private array $screensByName = []; + + public function __construct( + private readonly ProPresenterWorkspace $document, + ) { + $this->rebuildIndex(); + } + + public function getDocument(): ProPresenterWorkspace + { + return $this->document; + } + + /** @return Screen[] */ + public function getScreens(): array + { + return $this->screens; + } + + public function getScreenByName(string $name): ?Screen + { + return $this->screensByName[$name] ?? null; + } + + public function getScreenByUuid(string $uuid): ?Screen + { + return $this->screensByUuid[strtoupper($uuid)] ?? null; + } + + public function addScreen(Screen|ProPresenterScreen $screen): Screen + { + $proto = $screen instanceof Screen ? $screen->getProto() : $screen; + $existing = iterator_to_array($this->document->getProScreens()); + $existing[] = $proto; + $this->document->setProScreens($existing); + $this->rebuildIndex(); + + return $this->screens[array_key_last($this->screens)]; + } + + public function removeScreen(string $uuid): bool + { + $needle = strtoupper($uuid); + $kept = []; + $removed = false; + foreach ($this->document->getProScreens() as $proto) { + $current = strtoupper($proto->getUuid()?->getString() ?? ''); + if (!$removed && $current === $needle) { + $removed = true; + continue; + } + $kept[] = $proto; + } + + if (!$removed) { + return false; + } + + $this->document->setProScreens($kept); + $this->rebuildIndex(); + + return true; + } + + public function count(): int + { + return count($this->screens); + } + + public function getAudienceLooks(): RepeatedField + { + return $this->document->getAudienceLooks(); + } + + public function getMasks(): RepeatedField + { + return $this->document->getMasks(); + } + + public function getVideoInputs(): RepeatedField + { + return $this->document->getVideoInputs(); + } + + public function getSelectedLibraryName(): string + { + return $this->document->getSelectedLibraryName(); + } + + public function setSelectedLibraryName(string $name): self + { + $this->document->setSelectedLibraryName($name); + + return $this; + } + + private function rebuildIndex(): void + { + $this->screens = []; + $this->screensByUuid = []; + $this->screensByName = []; + + foreach ($this->document->getProScreens() as $proto) { + $screen = new Screen($proto); + $this->screens[] = $screen; + + $uuid = strtoupper($screen->getUuid()); + if ($uuid !== '') { + $this->screensByUuid[$uuid] = $screen; + } + + $name = $screen->getName(); + if ($name !== '') { + $this->screensByName[$name] ??= $screen; + } + } + } +} diff --git a/tests/CCLIFileReaderTest.php b/tests/CCLIFileReaderTest.php new file mode 100644 index 0000000..3d94ae6 --- /dev/null +++ b/tests/CCLIFileReaderTest.php @@ -0,0 +1,93 @@ +expectException(InvalidArgumentException::class); + CCLIFileReader::read(__DIR__ . '/../doc/reference_samples/does-not-exist-ccli'); + } + + #[Test] + public function readReturnsLibraryWithExpectedCount(): void + { + $library = CCLIFileReader::read(self::REFERENCE_PATH); + + $this->assertInstanceOf(CCLILibrary::class, $library); + $this->assertSame(1, $library->count()); + } + + #[Test] + public function documentExposesLicenseAndDisplaySettings(): void + { + $library = CCLIFileReader::read(self::REFERENCE_PATH); + + $this->assertTrue($library->isCCLIDisplayEnabled()); + $this->assertSame('', $library->getCCLILicense()); + $this->assertSame(0, $library->getDisplayType()); + $this->assertNotNull($library->getTemplate()); + } + + #[Test] + public function settersUpdateDocument(): void + { + $library = CCLIFileReader::read(self::REFERENCE_PATH); + + $library->setCCLILicense('1234567'); + $library->setDisplayType(3); + $library->setCCLIDisplayEnabled(false); + + $this->assertSame('1234567', $library->getCCLILicense()); + $this->assertSame(3, $library->getDisplayType()); + $this->assertFalse($library->isCCLIDisplayEnabled()); + } + + #[Test] + public function addAndRemoveRoundTrip(): void + { + $library = CCLIFileReader::read(self::REFERENCE_PATH); + + $template = $library->getTemplate(); + $this->assertNotNull($template); + + $library->setTemplate(null); + $this->assertNull($library->getTemplate()); + + $library->setTemplate($template); + $this->assertNotNull($library->getTemplate()); + } + + #[Test] + public function writerProducesByteIdenticalRoundTrip(): void + { + $original = file_get_contents(self::REFERENCE_PATH); + $library = CCLIFileReader::read(self::REFERENCE_PATH); + + $tmp = tempnam(sys_get_temp_dir(), 'ccli_'); + try { + CCLIFileWriter::write($library, $tmp); + $roundTrip = CCLIFileReader::read($tmp); + $this->assertSame(strlen((string) $original), strlen((string) file_get_contents($tmp))); + $this->assertSame($library->isCCLIDisplayEnabled(), $roundTrip->isCCLIDisplayEnabled()); + $this->assertSame($library->getCCLILicense(), $roundTrip->getCCLILicense()); + $this->assertSame($library->getDisplayType(), $roundTrip->getDisplayType()); + $this->assertSame($library->getTemplate() !== null, $roundTrip->getTemplate() !== null); + } finally { + @unlink($tmp); + } + } +} diff --git a/tests/CalendarFileReaderTest.php b/tests/CalendarFileReaderTest.php new file mode 100644 index 0000000..c93e636 --- /dev/null +++ b/tests/CalendarFileReaderTest.php @@ -0,0 +1,91 @@ +expectException(InvalidArgumentException::class); + CalendarFileReader::read(__DIR__ . '/../doc/reference_samples/does-not-exist-calendar'); + } + + #[Test] + public function readReturnsLibraryWithExpectedCount(): void + { + $library = CalendarFileReader::read(self::REFERENCE_PATH); + $this->assertInstanceOf(CalendarLibrary::class, $library); + $this->assertCount(3, $library->getEvents()); + $this->assertSame(3, $library->count()); + } + + #[Test] + public function eventsExposeNameAndUuid(): void + { + $first = CalendarFileReader::read(self::REFERENCE_PATH)->getEvents()[0]; + $this->assertInstanceOf(CalendarEvent::class, $first); + $this->assertSame('Doors Open', $first->getName()); + $this->assertSame('3E749EF4-0663-4F0F-AACA-BD801B6D8ACD', $first->getUuid()); + } + + #[Test] + public function eventsHaveExpectedNamesAndOpaqueBytes(): void + { + $events = CalendarFileReader::read(self::REFERENCE_PATH)->getEvents(); + $this->assertSame(['Doors Open', 'Godi Start', 'Doors Open'], array_map(static fn (CalendarEvent $event): string => $event->getName(), $events)); + foreach ($events as $event) { + $this->assertNotSame('', $event->getActionData()); + $this->assertNotSame('', $event->getMacroData()); + } + } + + #[Test] + public function lookupAndModeSucceed(): void + { + $library = CalendarFileReader::read(self::REFERENCE_PATH); + $event = $library->getEventByUuid('3e749ef4-0663-4f0f-aaca-bd801b6d8acd'); + $this->assertNotNull($event); + $this->assertSame('Doors Open', $event->getName()); + $this->assertSame(1, $library->getMode()); + $this->assertSame(1731833100, $event->getStartTimeSeconds()); + } + + #[Test] + public function addAndRemoveEventRoundTrip(): void + { + $library = CalendarFileReader::read(self::REFERENCE_PATH); + $library->addEvent('Test Event', '11111111-1111-1111-1111-111111111111'); + $this->assertSame(4, $library->count()); + $this->assertNotNull($library->getEventByUuid('11111111-1111-1111-1111-111111111111')); + $this->assertTrue($library->removeEvent('11111111-1111-1111-1111-111111111111')); + $this->assertSame(3, $library->count()); + } + + #[Test] + public function writerProducesByteIdenticalRoundTrip(): void + { + $first = tempnam(sys_get_temp_dir(), 'calendar_'); + $second = tempnam(sys_get_temp_dir(), 'calendar_'); + try { + CalendarFileWriter::write(CalendarFileReader::read(self::REFERENCE_PATH), $first); + CalendarFileWriter::write(CalendarFileReader::read($first), $second); + $this->assertSame(file_get_contents($first), file_get_contents($second)); + } finally { + @unlink($first ?: ''); + @unlink($second ?: ''); + } + } +} diff --git a/tests/ClearGroupsFileReaderTest.php b/tests/ClearGroupsFileReaderTest.php new file mode 100644 index 0000000..9761783 --- /dev/null +++ b/tests/ClearGroupsFileReaderTest.php @@ -0,0 +1,119 @@ +expectException(InvalidArgumentException::class); + ClearGroupsFileReader::read(__DIR__ . '/../doc/reference_samples/does-not-exist-clear-groups'); + } + + #[Test] + public function readReturnsLibraryWithExpectedCount(): void + { + $library = ClearGroupsFileReader::read(self::REFERENCE_PATH); + + $this->assertInstanceOf(ClearGroupsLibrary::class, $library); + $this->assertCount(1, $library->getGroups()); + $this->assertSame(1, $library->count()); + } + + #[Test] + public function clearGroupsExposeNameAndUuid(): void + { + $library = ClearGroupsFileReader::read(self::REFERENCE_PATH); + $first = $library->getGroups()[0]; + + $this->assertInstanceOf(ClearGroupDefinition::class, $first); + $this->assertSame('Alles ausblenden', $first->getName()); + $this->assertSame('A91C6AFE-098F-4559-B2CF-D8373C589589', $first->getUuid()); + } + + #[Test] + public function lookupByUuidIsCaseInsensitive(): void + { + $library = ClearGroupsFileReader::read(self::REFERENCE_PATH); + + $upper = $library->getClearGroupByUuid('A91C6AFE-098F-4559-B2CF-D8373C589589'); + $lower = $library->getClearGroupByUuid('a91c6afe-098f-4559-b2cf-d8373c589589'); + + $this->assertNotNull($upper); + $this->assertSame($upper, $lower); + $this->assertSame('Alles ausblenden', $upper->getName()); + } + + #[Test] + public function lookupByNameSucceeds(): void + { + $library = ClearGroupsFileReader::read(self::REFERENCE_PATH); + + $group = $library->getClearGroupByName('Alles ausblenden'); + $this->assertNotNull($group); + $this->assertSame('A91C6AFE-098F-4559-B2CF-D8373C589589', $group->getUuid()); + } + + #[Test] + public function colorIsExposedAsHex(): void + { + $library = ClearGroupsFileReader::read(self::REFERENCE_PATH); + $group = $library->getClearGroupByName('Alles ausblenden'); + + $this->assertNotNull($group); + $this->assertSame('#FFFFFF', $group->getColorHex()); + $this->assertFalse($group->isIconTinted()); + } + + #[Test] + public function setColorHexUpdatesProto(): void + { + $library = ClearGroupsFileReader::read(self::REFERENCE_PATH); + $group = $library->getGroups()[0]; + + $group->setColor(['r' => 1.0, 'g' => 0.0, 'b' => 0.0]); + $this->assertSame('#FF0000', $group->getColorHex()); + } + + #[Test] + public function addAndRemoveClearGroupRoundTrip(): void + { + $library = ClearGroupsFileReader::read(self::REFERENCE_PATH); + + $library->addClearGroup('Test Clear', '11111111-1111-1111-1111-111111111111'); + $this->assertNotNull($library->getClearGroupByUuid('11111111-1111-1111-1111-111111111111')); + $this->assertSame(2, $library->count()); + + $this->assertTrue($library->removeClearGroup('11111111-1111-1111-1111-111111111111')); + $this->assertNull($library->getClearGroupByUuid('11111111-1111-1111-1111-111111111111')); + $this->assertSame(1, $library->count()); + } + + #[Test] + public function writerProducesByteIdenticalRoundTrip(): void + { + $original = file_get_contents(self::REFERENCE_PATH); + $library = ClearGroupsFileReader::read(self::REFERENCE_PATH); + + $tmp = tempnam(sys_get_temp_dir(), 'clear_groups_'); + try { + ClearGroupsFileWriter::write($library, $tmp); + $this->assertSame($original, file_get_contents($tmp)); + } finally { + @unlink($tmp); + } + } +} diff --git a/tests/CommunicationDevicesFileReaderTest.php b/tests/CommunicationDevicesFileReaderTest.php new file mode 100644 index 0000000..deeea81 --- /dev/null +++ b/tests/CommunicationDevicesFileReaderTest.php @@ -0,0 +1,89 @@ +expectException(InvalidArgumentException::class); + CommunicationDevicesFileReader::read(__DIR__ . '/../doc/reference_samples/does-not-exist-communication-devices'); + } + + #[Test] + public function readReturnsLibraryWithExpectedCount(): void + { + $library = CommunicationDevicesFileReader::read(self::REFERENCE_PATH); + $this->assertInstanceOf(CommunicationDevicesLibrary::class, $library); + $this->assertCount(0, $library->getDevices()); + $this->assertSame(0, $library->count()); + } + + #[Test] + public function emptyListCanAddDevice(): void + { + $library = CommunicationDevicesFileReader::read(self::REFERENCE_PATH); + $device = (new CommunicationDevice()) + ->setId('device-1') + ->setName('Lighting Console') + ->setType('network') + ->setAddress('192.0.2.10'); + $library->addDevice($device); + $this->assertSame(1, $library->count()); + $this->assertSame('Lighting Console', $library->getDevices()[0]->getName()); + $this->assertSame('device-1', $library->getDevices()[0]->getId()); + } + + #[Test] + public function writeReadRoundTripPreservesDeviceSemantics(): void + { + $library = new CommunicationDevicesLibrary(); + $library->addDevice((new CommunicationDevice())->setId('device-1')->setName('Stage Router')->setType('tcp')->setAddress('10.0.0.5')); + $tmp = tempnam(sys_get_temp_dir(), 'devices_'); + try { + CommunicationDevicesFileWriter::write($library, $tmp); + $roundTrip = CommunicationDevicesFileReader::read($tmp); + $this->assertSame($library->getDocument(), $roundTrip->getDocument()); + } finally { + @unlink($tmp ?: ''); + } + } + + #[Test] + public function addAndRemoveDeviceRoundTrip(): void + { + $library = CommunicationDevicesFileReader::read(self::REFERENCE_PATH); + $library->addDevice((new CommunicationDevice())->setId('device-1')->setName('Stage Router')); + $this->assertSame(1, $library->count()); + $this->assertTrue($library->removeDevice('device-1')); + $this->assertSame(0, $library->count()); + } + + #[Test] + public function writerProducesByteIdenticalRoundTrip(): void + { + $first = tempnam(sys_get_temp_dir(), 'devices_'); + $second = tempnam(sys_get_temp_dir(), 'devices_'); + try { + CommunicationDevicesFileWriter::write(CommunicationDevicesFileReader::read(self::REFERENCE_PATH), $first); + CommunicationDevicesFileWriter::write(CommunicationDevicesFileReader::read($first), $second); + $this->assertSame(json_decode((string) file_get_contents($first), true), json_decode((string) file_get_contents($second), true)); + } finally { + @unlink($first ?: ''); + @unlink($second ?: ''); + } + } +} diff --git a/tests/GroupsFileReaderTest.php b/tests/GroupsFileReaderTest.php new file mode 100644 index 0000000..556e955 --- /dev/null +++ b/tests/GroupsFileReaderTest.php @@ -0,0 +1,124 @@ +expectException(InvalidArgumentException::class); + GroupsFileReader::read(__DIR__ . '/../doc/reference_samples/does-not-exist-groups'); + } + + #[Test] + public function readReturnsLibraryWithExpectedCount(): void + { + $library = GroupsFileReader::read(self::REFERENCE_PATH); + + $this->assertInstanceOf(GroupLibrary::class, $library); + $this->assertCount(29, $library->getGroups()); + $this->assertSame(29, $library->count()); + } + + #[Test] + public function groupsExposeNameAndUuid(): void + { + $library = GroupsFileReader::read(self::REFERENCE_PATH); + $first = $library->getGroups()[0]; + + $this->assertInstanceOf(GroupDefinition::class, $first); + $this->assertSame('Vers', $first->getName()); + $this->assertSame('4E9D56A2-7E96-4975-97CC-44982257EF8A', $first->getUuid()); + } + + #[Test] + public function lookupByUuidIsCaseInsensitive(): void + { + $library = GroupsFileReader::read(self::REFERENCE_PATH); + + $upper = $library->getGroupByUuid('4E9D56A2-7E96-4975-97CC-44982257EF8A'); + $lower = $library->getGroupByUuid('4e9d56a2-7e96-4975-97cc-44982257ef8a'); + + $this->assertNotNull($upper); + $this->assertSame($upper, $lower); + $this->assertSame('Vers', $upper->getName()); + } + + #[Test] + public function lookupByNameSucceeds(): void + { + $library = GroupsFileReader::read(self::REFERENCE_PATH); + + $verse1 = $library->getGroupByName('Verse 1'); + $this->assertNotNull($verse1); + $this->assertSame('1D85C82C-EC82-44D8-8ED0-7742D46242C0', $verse1->getUuid()); + } + + #[Test] + public function colorIsExposedAsHex(): void + { + $library = GroupsFileReader::read(self::REFERENCE_PATH); + + $vers = $library->getGroupByName('Vers'); + $this->assertNotNull($vers); + $this->assertSame('#0077CC', $vers->getColorHex()); + + $color = $vers->getColor(); + $this->assertNotNull($color); + $this->assertEqualsWithDelta(1.0, $color['a'], 0.001); + } + + #[Test] + public function setColorHexUpdatesProto(): void + { + $library = GroupsFileReader::read(self::REFERENCE_PATH); + + $vers = $library->getGroupByName('Vers'); + $this->assertNotNull($vers); + + $vers->setColor(['r' => 1.0, 'g' => 0.0, 'b' => 0.0]); + $this->assertSame('#FF0000', $vers->getColorHex()); + } + + #[Test] + public function addAndRemoveGroupRoundTrip(): void + { + $library = GroupsFileReader::read(self::REFERENCE_PATH); + + $library->addGroup('Test Group', '11111111-1111-1111-1111-111111111111'); + $this->assertNotNull($library->getGroupByUuid('11111111-1111-1111-1111-111111111111')); + $this->assertSame(30, $library->count()); + + $this->assertTrue($library->removeGroup('11111111-1111-1111-1111-111111111111')); + $this->assertNull($library->getGroupByUuid('11111111-1111-1111-1111-111111111111')); + $this->assertSame(29, $library->count()); + } + + #[Test] + public function writerProducesByteIdenticalRoundTrip(): void + { + $original = file_get_contents(self::REFERENCE_PATH); + $library = GroupsFileReader::read(self::REFERENCE_PATH); + + $tmp = tempnam(sys_get_temp_dir(), 'groups_'); + try { + GroupsFileWriter::write($library, $tmp); + $this->assertSame($original, file_get_contents($tmp)); + } finally { + @unlink($tmp); + } + } +} diff --git a/tests/KeyMappingsFileReaderTest.php b/tests/KeyMappingsFileReaderTest.php new file mode 100644 index 0000000..88d6cee --- /dev/null +++ b/tests/KeyMappingsFileReaderTest.php @@ -0,0 +1,103 @@ +expectException(InvalidArgumentException::class); + KeyMappingsFileReader::read(__DIR__ . '/../doc/reference_samples/does-not-exist-key-mappings'); + } + + #[Test] + public function readReturnsLibraryWithExpectedCount(): void + { + $library = KeyMappingsFileReader::read(self::REFERENCE_PATH); + + $this->assertInstanceOf(KeyMappingsLibrary::class, $library); + $this->assertCount(0, $library->getMappings()); + $this->assertSame(0, $library->count()); + $this->assertNotNull($library->getApplicationInfo()); + } + + #[Test] + public function mappingsExposeNameAndUuid(): void + { + $library = KeyMappingsFileReader::read(self::REFERENCE_PATH); + $mapping = $library->addMapping('Test Mapping', 'ABCDEFAB-1111-1111-1111-111111111111', 'target'); + + $this->assertInstanceOf(KeyMapping::class, $mapping); + $this->assertSame('Test Mapping', $mapping->getName()); + $this->assertSame('ABCDEFAB-1111-1111-1111-111111111111', $mapping->getUuid()); + $this->assertSame('target', $mapping->getTarget()); + } + + #[Test] + public function lookupByUuidIsCaseInsensitive(): void + { + $library = KeyMappingsFileReader::read(self::REFERENCE_PATH); + + $library->addMapping('Test Mapping', 'ABCDEFAB-1111-1111-1111-111111111111'); + $upper = $library->getMappingByUuid('ABCDEFAB-1111-1111-1111-111111111111'); + $lower = $library->getMappingByUuid('abcdefab-1111-1111-1111-111111111111'); + + $this->assertNotNull($upper); + $this->assertSame($upper, $lower); + $this->assertSame('Test Mapping', $upper->getName()); + } + + #[Test] + public function lookupByNameSucceeds(): void + { + $library = KeyMappingsFileReader::read(self::REFERENCE_PATH); + + $library->addMapping('Test Mapping', 'ABCDEFAB-1111-1111-1111-111111111111'); + $mapping = $library->getMappingByName('Test Mapping'); + + $this->assertNotNull($mapping); + $this->assertSame('ABCDEFAB-1111-1111-1111-111111111111', $mapping->getUuid()); + } + + #[Test] + public function addAndRemoveMappingRoundTrip(): void + { + $library = KeyMappingsFileReader::read(self::REFERENCE_PATH); + + $library->addMapping('Test Mapping', 'ABCDEFAB-1111-1111-1111-111111111111'); + $this->assertNotNull($library->getMappingByUuid('ABCDEFAB-1111-1111-1111-111111111111')); + $this->assertSame(1, $library->count()); + + $this->assertTrue($library->removeMapping('abcdefab-1111-1111-1111-111111111111')); + $this->assertNull($library->getMappingByUuid('ABCDEFAB-1111-1111-1111-111111111111')); + $this->assertSame(0, $library->count()); + } + + #[Test] + public function writerProducesByteIdenticalRoundTrip(): void + { + $original = file_get_contents(self::REFERENCE_PATH); + $library = KeyMappingsFileReader::read(self::REFERENCE_PATH); + + $tmp = tempnam(sys_get_temp_dir(), 'key_mappings_'); + try { + KeyMappingsFileWriter::write($library, $tmp); + $this->assertSame($original, file_get_contents($tmp)); + } finally { + @unlink($tmp); + } + } +} diff --git a/tests/LabelsFileWriterTest.php b/tests/LabelsFileWriterTest.php new file mode 100644 index 0000000..45b4466 --- /dev/null +++ b/tests/LabelsFileWriterTest.php @@ -0,0 +1,71 @@ +assertSame($original, file_get_contents($tmp)); + } finally { + @unlink($tmp); + } + } + + #[Test] + public function addLabelPersistsThroughWriteRead(): void + { + $library = LabelsFileReader::read(self::REFERENCE_PATH); + $library->addLabel('CustomLabel', ['r' => 0.5, 'g' => 0.5, 'b' => 0.5, 'a' => 1.0]); + + $tmp = tempnam(sys_get_temp_dir(), 'labels_'); + try { + LabelsFileWriter::write($library, $tmp); + + $reload = LabelsFileReader::read($tmp); + $custom = $reload->getLabelByName('CustomLabel'); + $this->assertNotNull($custom); + $this->assertSame('#808080', $custom->getColorHex()); + } finally { + @unlink($tmp); + } + } + + #[Test] + public function setColorHexAcceptsRrggbb(): void + { + $library = LabelsFileReader::read(self::REFERENCE_PATH); + $beamer = $library->getLabelByName('KeyVisual Beamer'); + $this->assertNotNull($beamer); + + $beamer->setColorHex('#FF8800'); + $this->assertSame('#FF8800', $beamer->getColorHex()); + } + + #[Test] + public function removeLabelRemovesFromDocument(): void + { + $library = LabelsFileReader::read(self::REFERENCE_PATH); + $countBefore = $library->count(); + + $this->assertTrue($library->removeLabel('Leere Folie')); + $this->assertSame($countBefore - 1, $library->count()); + $this->assertNull($library->getLabelByName('Leere Folie')); + } +} diff --git a/tests/MacrosFileWriterTest.php b/tests/MacrosFileWriterTest.php new file mode 100644 index 0000000..e5aa091 --- /dev/null +++ b/tests/MacrosFileWriterTest.php @@ -0,0 +1,83 @@ +assertSame(strlen($original), strlen($written)); + } finally { + @unlink($tmp); + } + } + + #[Test] + public function readBackPreservesMacrosAndCollections(): void + { + $library = MacrosFileReader::read(self::REFERENCE_PATH); + + $tmp = tempnam(sys_get_temp_dir(), 'macros_'); + try { + MacrosFileWriter::write($library, $tmp); + $reload = MacrosFileReader::read($tmp); + + $this->assertCount(24, $reload->getMacros()); + $this->assertCount(3, $reload->getCollections()); + $this->assertNotNull($reload->getMacroByName('Gottesdienst START')); + } finally { + @unlink($tmp); + } + } + + #[Test] + public function addMacroPersistsThroughWriteRead(): void + { + $library = MacrosFileReader::read(self::REFERENCE_PATH); + $library->addMacro('TestMacro', '11111111-1111-1111-1111-111111111111'); + + $tmp = tempnam(sys_get_temp_dir(), 'macros_'); + try { + MacrosFileWriter::write($library, $tmp); + $reload = MacrosFileReader::read($tmp); + + $found = $reload->getMacroByUuid('11111111-1111-1111-1111-111111111111'); + $this->assertNotNull($found); + $this->assertSame('TestMacro', $found->getName()); + } finally { + @unlink($tmp); + } + } + + #[Test] + public function setColorHexUpdatesMacro(): void + { + $library = MacrosFileReader::read(self::REFERENCE_PATH); + $macro = $library->getMacroByName('Gottesdienst START'); + $this->assertNotNull($macro); + + $macro->setColor(['r' => 0.5, 'g' => 0.0, 'b' => 1.0]); + $color = $macro->getColor(); + $this->assertNotNull($color); + $this->assertEqualsWithDelta(0.5, $color['r'], 0.001); + $this->assertEqualsWithDelta(0.0, $color['g'], 0.001); + $this->assertEqualsWithDelta(1.0, $color['b'], 0.001); + } +} diff --git a/tests/MessagesFileReaderTest.php b/tests/MessagesFileReaderTest.php new file mode 100644 index 0000000..dbc325d --- /dev/null +++ b/tests/MessagesFileReaderTest.php @@ -0,0 +1,88 @@ +expectException(InvalidArgumentException::class); + MessagesFileReader::read(__DIR__ . '/../doc/reference_samples/does-not-exist-messages'); + } + + #[Test] + public function readReturnsLibraryWithExpectedCount(): void + { + $library = MessagesFileReader::read(self::REFERENCE_PATH); + $this->assertInstanceOf(MessageLibrary::class, $library); + $this->assertCount(2, $library->getMessages()); + $this->assertSame(2, $library->count()); + } + + #[Test] + public function messagesExposeTitleAndUuid(): void + { + $first = MessagesFileReader::read(self::REFERENCE_PATH)->getMessages()[0]; + $this->assertInstanceOf(Message::class, $first); + $this->assertSame('Gottesdienst Sonntag 10Uhr Timer', $first->getTitle()); + $this->assertSame('5D1DAC57-CD17-4AD0-A096-CCCB37FF425B', $first->getUuid()); + } + + #[Test] + public function lookupByUuidIsCaseInsensitive(): void + { + $library = MessagesFileReader::read(self::REFERENCE_PATH); + $upper = $library->getMessageByUuid('5D1DAC57-CD17-4AD0-A096-CCCB37FF425B'); + $lower = $library->getMessageByUuid('5d1dac57-cd17-4ad0-a096-cccb37ff425b'); + $this->assertNotNull($upper); + $this->assertSame($upper, $lower); + } + + #[Test] + public function lookupByNameSucceeds(): void + { + $message = MessagesFileReader::read(self::REFERENCE_PATH)->getMessageByName('Neue Nachricht'); + $this->assertNotNull($message); + $this->assertSame('68F5B8E9-7EA8-4259-A990-D1863BC56C78', $message->getUuid()); + } + + #[Test] + public function addAndRemoveMessageRoundTrip(): void + { + $library = MessagesFileReader::read(self::REFERENCE_PATH); + $library->addMessage('Test Message', '11111111-1111-1111-1111-111111111111'); + $this->assertSame(3, $library->count()); + $this->assertNotNull($library->getMessageByUuid('11111111-1111-1111-1111-111111111111')); + $this->assertTrue($library->removeMessage('11111111-1111-1111-1111-111111111111')); + $this->assertSame(2, $library->count()); + } + + #[Test] + public function writerProducesByteIdenticalRoundTrip(): void + { + $library = MessagesFileReader::read(self::REFERENCE_PATH); + $first = tempnam(sys_get_temp_dir(), 'messages_'); + $second = tempnam(sys_get_temp_dir(), 'messages_'); + try { + MessagesFileWriter::write($library, $first); + MessagesFileWriter::write(MessagesFileReader::read($first), $second); + $this->assertSame(file_get_contents($first), file_get_contents($second)); + } finally { + @unlink($first ?: ''); + @unlink($second ?: ''); + } + } +} diff --git a/tests/PropsFileReaderTest.php b/tests/PropsFileReaderTest.php new file mode 100644 index 0000000..fc96306 --- /dev/null +++ b/tests/PropsFileReaderTest.php @@ -0,0 +1,83 @@ +expectException(InvalidArgumentException::class); + PropsFileReader::read(__DIR__ . '/../doc/reference_samples/does-not-exist-props'); + } + + #[Test] + public function readReturnsLibraryWithExpectedCount(): void + { + $library = PropsFileReader::read(self::REFERENCE_PATH); + $this->assertInstanceOf(PropLibrary::class, $library); + $this->assertCount(13, $library->getProps()); + $this->assertSame(13, $library->count()); + } + + #[Test] + public function propExposesNameAndUuid(): void + { + $prop = PropsFileReader::read(self::REFERENCE_PATH)->getProps()[0]; + $this->assertInstanceOf(Prop::class, $prop); + $this->assertSame('Props #1', $prop->getName()); + $this->assertSame('1FB23674-4341-4257-A376-E7E7318E84EF', $prop->getUuid()); + $this->assertTrue($prop->isEnabled()); + } + + #[Test] + public function lookupByUuidIsCaseInsensitive(): void + { + $library = PropsFileReader::read(self::REFERENCE_PATH); + $upper = $library->getPropByUuid('1FB23674-4341-4257-A376-E7E7318E84EF'); + $lower = $library->getPropByUuid('1fb23674-4341-4257-a376-e7e7318e84ef'); + $this->assertNotNull($upper); + $this->assertSame($upper, $lower); + } + + #[Test] + public function writerProducesStableRoundTrip(): void + { + $tmp = tempnam(sys_get_temp_dir(), 'props_'); + $second = tempnam(sys_get_temp_dir(), 'props_'); + try { + PropsFileWriter::write(PropsFileReader::read(self::REFERENCE_PATH), $tmp); + PropsFileWriter::write(PropsFileReader::read($tmp), $second); + $this->assertSame(file_get_contents($tmp), file_get_contents($second)); + } finally { + @unlink($tmp); + @unlink($second); + } + } + + #[Test] + public function addAndRemovePropRoundTrip(): void + { + $library = PropsFileReader::read(self::REFERENCE_PATH); + $prop = new Prop(new Cue()); + $prop->setName('Test Prop')->setUuid('11111111-1111-1111-1111-111111111111')->setEnabled(true); + $library->addProp($prop); + $this->assertSame(14, $library->count()); + $this->assertSame('Test Prop', $library->getPropByUuid('11111111-1111-1111-1111-111111111111')?->getName()); + $this->assertTrue($library->removeProp('11111111-1111-1111-1111-111111111111')); + $this->assertSame(13, $library->count()); + } +} diff --git a/tests/StageFileReaderTest.php b/tests/StageFileReaderTest.php new file mode 100644 index 0000000..3ae1ac0 --- /dev/null +++ b/tests/StageFileReaderTest.php @@ -0,0 +1,86 @@ +expectException(InvalidArgumentException::class); + StageFileReader::read(__DIR__ . '/../doc/reference_samples/does-not-exist-stage'); + } + + #[Test] + public function readReturnsLibraryWithExpectedCount(): void + { + $library = StageFileReader::read(self::REFERENCE_PATH); + $this->assertInstanceOf(StageLibrary::class, $library); + $this->assertCount(12, $library->getLayouts()); + $this->assertSame(12, $library->count()); + } + + #[Test] + public function layoutExposesNameAndUuid(): void + { + $layout = StageFileReader::read(self::REFERENCE_PATH)->getLayouts()[0]; + $this->assertInstanceOf(StageLayout::class, $layout); + $this->assertSame('Default StageDisplay', $layout->getName()); + $this->assertSame('0455674A-3F5C-4A62-B944-C276F3DF6F4E', $layout->getUuid()); + $this->assertNotNull($layout->getSlide()); + } + + #[Test] + public function lookupByUuidIsCaseInsensitive(): void + { + $library = StageFileReader::read(self::REFERENCE_PATH); + $upper = $library->getLayoutByUuid('0455674A-3F5C-4A62-B944-C276F3DF6F4E'); + $lower = $library->getLayoutByUuid('0455674a-3f5c-4a62-b944-c276f3df6f4e'); + $this->assertNotNull($upper); + $this->assertSame($upper, $lower); + } + + #[Test] + public function writerProducesStableRoundTrip(): void + { + $library = StageFileReader::read(self::REFERENCE_PATH); + $tmp = tempnam(sys_get_temp_dir(), 'stage_'); + $second = tempnam(sys_get_temp_dir(), 'stage_'); + try { + StageFileWriter::write($library, $tmp); + StageFileWriter::write(StageFileReader::read($tmp), $second); + $this->assertSame(file_get_contents($tmp), file_get_contents($second)); + } finally { + @unlink($tmp); + @unlink($second); + } + } + + #[Test] + public function addAndRemoveLayoutRoundTrip(): void + { + $library = StageFileReader::read(self::REFERENCE_PATH); + $layout = new StageLayout(new LayoutProto()); + $layout->setName('Test Layout')->setUuid('11111111-1111-1111-1111-111111111111'); + + $library->addLayout($layout); + $this->assertSame(13, $library->count()); + $this->assertSame('Test Layout', $library->getLayoutByUuid('11111111-1111-1111-1111-111111111111')?->getName()); + + $this->assertTrue($library->removeLayout('11111111-1111-1111-1111-111111111111')); + $this->assertSame(12, $library->count()); + } +} diff --git a/tests/TestPatternsFileReaderTest.php b/tests/TestPatternsFileReaderTest.php new file mode 100644 index 0000000..acba56c --- /dev/null +++ b/tests/TestPatternsFileReaderTest.php @@ -0,0 +1,99 @@ +expectException(InvalidArgumentException::class); + TestPatternsFileReader::read(__DIR__ . '/../doc/reference_samples/does-not-exist-test-patterns'); + } + + #[Test] + public function readReturnsLibraryWithExpectedCount(): void + { + $library = TestPatternsFileReader::read(self::REFERENCE_PATH); + + $this->assertInstanceOf(TestPatternsLibrary::class, $library); + $this->assertCount(0, $library->getPatterns()); + $this->assertSame(0, $library->count()); + } + + #[Test] + public function stateExposesDisplayLocationAndSpecificScreenUuid(): void + { + $library = TestPatternsFileReader::read(self::REFERENCE_PATH); + + $this->assertNotNull($library->getState()); + $this->assertSame(3, $library->getDisplayLocation()); + $this->assertSame('BCDE1115-AD40-4BA4-A33A-BFFE3E87223B', $library->getSpecificScreenUuid()); + } + + #[Test] + public function lookupByUuidIsCaseInsensitive(): void + { + $library = TestPatternsFileReader::read(self::REFERENCE_PATH); + + $library->addPattern('Test Pattern', 'ABCDEFAB-1111-1111-1111-111111111111'); + $upper = $library->getPatternByUuid('ABCDEFAB-1111-1111-1111-111111111111'); + $lower = $library->getPatternByUuid('abcdefab-1111-1111-1111-111111111111'); + + $this->assertNotNull($upper); + $this->assertSame($upper, $lower); + $this->assertSame('Test Pattern', $upper->getNameLocalizationKey()); + } + + #[Test] + public function lookupByNameSucceeds(): void + { + $library = TestPatternsFileReader::read(self::REFERENCE_PATH); + + $library->addPattern('Test Pattern', '11111111-1111-1111-1111-111111111111'); + $pattern = $library->getPatternByName('Test Pattern'); + + $this->assertNotNull($pattern); + $this->assertSame('11111111-1111-1111-1111-111111111111', $pattern->getUuid()?->getString()); + } + + #[Test] + public function addAndRemovePatternRoundTrip(): void + { + $library = TestPatternsFileReader::read(self::REFERENCE_PATH); + + $library->addPattern('Test Pattern', '11111111-1111-1111-1111-111111111111'); + $this->assertNotNull($library->getPatternByUuid('11111111-1111-1111-1111-111111111111')); + $this->assertSame(1, $library->count()); + + $this->assertTrue($library->removePattern('11111111-1111-1111-1111-111111111111')); + $this->assertNull($library->getPatternByUuid('11111111-1111-1111-1111-111111111111')); + $this->assertSame(0, $library->count()); + } + + #[Test] + public function writerProducesByteIdenticalRoundTrip(): void + { + $original = file_get_contents(self::REFERENCE_PATH); + $library = TestPatternsFileReader::read(self::REFERENCE_PATH); + + $tmp = tempnam(sys_get_temp_dir(), 'test_patterns_'); + try { + TestPatternsFileWriter::write($library, $tmp); + $this->assertSame($original, file_get_contents($tmp)); + } finally { + @unlink($tmp); + } + } +} diff --git a/tests/ThemeFileReaderTest.php b/tests/ThemeFileReaderTest.php new file mode 100644 index 0000000..db488e2 --- /dev/null +++ b/tests/ThemeFileReaderTest.php @@ -0,0 +1,148 @@ +expectException(InvalidArgumentException::class); + ThemeFileReader::read(__DIR__ . '/../doc/reference_samples/pp-themes/does-not-exist'); + } + + #[Test] + public function readReturnsBundleWithExpectedCount(): void + { + $bundle = ThemeFileReader::read(self::REFERENCE_PATH); + $this->assertInstanceOf(ThemeBundle::class, $bundle); + $this->assertCount(11, $bundle->getSlides()); + $this->assertSame(11, $bundle->count()); + $this->assertCount(3, $bundle->getAssets()); + $this->assertSame(3, $bundle->getAssetCount()); + } + + #[Test] + public function slideExposesNameAndBaseSlide(): void + { + $slide = ThemeFileReader::read(self::REFERENCE_PATH)->getSlides()[0]; + $this->assertInstanceOf(ThemeSlide::class, $slide); + $this->assertSame('KeyVisual', $slide->getName()); + $this->assertNotNull($slide->getBaseSlide()); + } + + #[Test] + public function assetsExposeBytesAndMimeType(): void + { + $asset = ThemeFileReader::read(self::REFERENCE_PATH)->getAssetByName('BACKGROUND.jpg'); + $this->assertInstanceOf(ThemeAsset::class, $asset); + $this->assertSame('BACKGROUND.jpg', $asset->getName()); + $this->assertNotSame('', $asset->getBytes()); + $this->assertGreaterThan(0, $asset->getSize()); + $this->assertSame('image/jpeg', $asset->getMimeType()); + } + + #[Test] + public function writerProducesStableThemeDocumentRoundTrip(): void + { + $tmp = $this->makeTempDir('theme_'); + $second = $this->makeTempDir('theme_'); + try { + ThemeFileWriter::write(ThemeFileReader::read(self::REFERENCE_PATH), $tmp); + ThemeFileWriter::write(ThemeFileReader::read($tmp), $second); + $this->assertSame(file_get_contents($tmp . '/Theme'), file_get_contents($second . '/Theme')); + } finally { + $this->removeDirectory($tmp); + $this->removeDirectory($second); + } + } + + #[Test] + public function writerRoundTripsEntireFolder(): void + { + $source = ThemeFileReader::read(self::REFERENCE_PATH); + $tmp = $this->makeTempDir('theme_'); + try { + ThemeFileWriter::write($source, $tmp); + $roundTrip = ThemeFileReader::read($tmp); + $this->assertSame($source->count(), $roundTrip->count()); + $this->assertSame($source->getAssetCount(), $roundTrip->getAssetCount()); + $first = $source->getAssets()[0]; + $this->assertSame($first->getBytes(), $roundTrip->getAssetByName($first->getName())?->getBytes()); + } finally { + $this->removeDirectory($tmp); + } + } + + #[Test] + public function addRemoveAndStaleAssetCleanupRoundTrip(): void + { + $bundle = ThemeFileReader::read(self::REFERENCE_PATH); + $slide = new ThemeSlide(new TemplateSlide()); + $slide->setName('Test Theme Slide'); + $bundle->addSlide($slide); + $bundle->addAsset('TEST.png', 'png-bytes'); + $this->assertSame(12, $bundle->count()); + $this->assertSame(4, $bundle->getAssetCount()); + + $this->assertTrue($bundle->removeSlide('Test Theme Slide')); + $this->assertTrue($bundle->removeAsset('TEST.png')); + $this->assertSame(11, $bundle->count()); + $this->assertSame(3, $bundle->getAssetCount()); + + $tmp = $this->makeTempDir('theme_'); + try { + mkdir($tmp . '/Assets'); + file_put_contents($tmp . '/Assets/stale.jpg', 'stale'); + ThemeFileWriter::write($bundle, $tmp); + $this->assertFileDoesNotExist($tmp . '/Assets/stale.jpg'); + } finally { + $this->removeDirectory($tmp); + } + } + + private function makeTempDir(string $prefix): string + { + $path = sys_get_temp_dir() . '/' . $prefix . uniqid('', true); + mkdir($path); + + return $path; + } + + private function removeDirectory(string $path): void + { + if (!is_dir($path)) { + return; + } + $entries = scandir($path); + if ($entries === false) { + return; + } + foreach ($entries as $entry) { + if ($entry === '.' || $entry === '..') { + continue; + } + $child = $path . '/' . $entry; + if (is_dir($child)) { + $this->removeDirectory($child); + } else { + @unlink($child); + } + } + @rmdir($path); + } +} diff --git a/tests/TimersFileReaderTest.php b/tests/TimersFileReaderTest.php new file mode 100644 index 0000000..fc0d7bd --- /dev/null +++ b/tests/TimersFileReaderTest.php @@ -0,0 +1,94 @@ +expectException(InvalidArgumentException::class); + TimersFileReader::read(__DIR__ . '/../doc/reference_samples/does-not-exist-timers'); + } + + #[Test] + public function readReturnsLibraryWithExpectedCount(): void + { + $library = TimersFileReader::read(self::REFERENCE_PATH); + $this->assertInstanceOf(TimersLibrary::class, $library); + $this->assertCount(5, $library->getTimers()); + $this->assertSame(5, $library->count()); + } + + #[Test] + public function timersExposeNameAndUuid(): void + { + $first = TimersFileReader::read(self::REFERENCE_PATH)->getTimers()[0]; + $this->assertInstanceOf(Timer::class, $first); + $this->assertSame('Gottesdienst (10:02)', $first->getName()); + $this->assertSame('0E45D0AF-BCC2-4A31-BCFD-0F5A3358E225', $first->getUuid()); + } + + #[Test] + public function lookupByUuidIsCaseInsensitiveAndClockFormatIsExposed(): void + { + $library = TimersFileReader::read(self::REFERENCE_PATH); + $upper = $library->getTimerByUuid('0E45D0AF-BCC2-4A31-BCFD-0F5A3358E225'); + $lower = $library->getTimerByUuid('0e45d0af-bcc2-4a31-bcfd-0f5a3358e225'); + $this->assertNotNull($upper); + $this->assertSame($upper, $lower); + $this->assertSame('HH:mm', $library->getClockFormat()); + } + + #[Test] + public function timerTypesAreIdentified(): void + { + $library = TimersFileReader::read(self::REFERENCE_PATH); + $service = $library->getTimerByName('Gottesdienst (10:02)'); + $five = $library->getTimerByName('5 Minuten Countdown'); + $this->assertNotNull($service); + $this->assertTrue($service->isCountdownToTime()); + $this->assertFalse($service->isCountdown()); + $this->assertNotNull($five); + $this->assertTrue($five->isCountdown()); + $this->assertSame(300, $five->getDurationSeconds()); + } + + #[Test] + public function addAndRemoveTimerRoundTrip(): void + { + $library = TimersFileReader::read(self::REFERENCE_PATH); + $library->addTimer('Test Timer', '11111111-1111-1111-1111-111111111111'); + $this->assertSame(6, $library->count()); + $this->assertNotNull($library->getTimerByUuid('11111111-1111-1111-1111-111111111111')); + $this->assertTrue($library->removeTimer('11111111-1111-1111-1111-111111111111')); + $this->assertSame(5, $library->count()); + } + + #[Test] + public function writerProducesByteIdenticalRoundTrip(): void + { + $first = tempnam(sys_get_temp_dir(), 'timers_'); + $second = tempnam(sys_get_temp_dir(), 'timers_'); + try { + TimersFileWriter::write(TimersFileReader::read(self::REFERENCE_PATH), $first); + TimersFileWriter::write(TimersFileReader::read($first), $second); + $this->assertSame(file_get_contents($first), file_get_contents($second)); + } finally { + @unlink($first ?: ''); + @unlink($second ?: ''); + } + } +} diff --git a/tests/WorkspaceFileReaderTest.php b/tests/WorkspaceFileReaderTest.php new file mode 100644 index 0000000..770c152 --- /dev/null +++ b/tests/WorkspaceFileReaderTest.php @@ -0,0 +1,83 @@ +expectException(InvalidArgumentException::class); + WorkspaceFileReader::read(__DIR__ . '/../doc/reference_samples/does-not-exist-workspace'); + } + + #[Test] + public function readReturnsLibraryWithExpectedCount(): void + { + $library = WorkspaceFileReader::read(self::REFERENCE_PATH); + $this->assertInstanceOf(WorkspaceLibrary::class, $library); + $this->assertCount(5, $library->getScreens()); + $this->assertSame(5, $library->count()); + } + + #[Test] + public function screenExposesNameAndUuid(): void + { + $screen = WorkspaceFileReader::read(self::REFERENCE_PATH)->getScreens()[0]; + $this->assertInstanceOf(Screen::class, $screen); + $this->assertSame('StageDisplay', $screen->getName()); + $this->assertSame('C86D614D-9441-4F78-A177-03E6E5FFEDF8', $screen->getUuid()); + $this->assertSame(2, $screen->getScreenType()); + } + + #[Test] + public function lookupByUuidIsCaseInsensitive(): void + { + $library = WorkspaceFileReader::read(self::REFERENCE_PATH); + $upper = $library->getScreenByUuid('C86D614D-9441-4F78-A177-03E6E5FFEDF8'); + $lower = $library->getScreenByUuid('c86d614d-9441-4f78-a177-03e6e5ffedf8'); + $this->assertNotNull($upper); + $this->assertSame($upper, $lower); + } + + #[Test] + public function writerProducesStableRoundTrip(): void + { + $tmp = tempnam(sys_get_temp_dir(), 'workspace_'); + $second = tempnam(sys_get_temp_dir(), 'workspace_'); + try { + WorkspaceFileWriter::write(WorkspaceFileReader::read(self::REFERENCE_PATH), $tmp); + WorkspaceFileWriter::write(WorkspaceFileReader::read($tmp), $second); + $this->assertSame(file_get_contents($tmp), file_get_contents($second)); + } finally { + @unlink($tmp); + @unlink($second); + } + } + + #[Test] + public function addAndRemoveScreenRoundTrip(): void + { + $library = WorkspaceFileReader::read(self::REFERENCE_PATH); + $screen = new Screen(new ProPresenterScreen()); + $screen->setName('Test Screen')->setUuid('11111111-1111-1111-1111-111111111111'); + $library->addScreen($screen); + $this->assertSame(6, $library->count()); + $this->assertSame('Test Screen', $library->getScreenByUuid('11111111-1111-1111-1111-111111111111')?->getName()); + $this->assertTrue($library->removeScreen('11111111-1111-1111-1111-111111111111')); + $this->assertSame(5, $library->count()); + } +}