227 lines
5.9 KiB
Markdown
227 lines
5.9 KiB
Markdown
# Bundle Parser API
|
|
|
|
> PHP module for reading, modifying, and writing ProPresenter `.probundle` files.
|
|
|
|
## Quick Reference
|
|
|
|
```php
|
|
use ProPresenter\Parser\ProBundleReader;
|
|
use ProPresenter\Parser\ProBundleWriter;
|
|
use ProPresenter\Parser\PresentationBundle;
|
|
|
|
// Read
|
|
$bundle = ProBundleReader::read('path/to/presentation.probundle');
|
|
|
|
// Access
|
|
$bundle->getName(); // Presentation name
|
|
$bundle->getSong(); // Song wrapper
|
|
$bundle->getMediaFiles(); // ['path' => bytes, ...]
|
|
|
|
// Write
|
|
ProBundleWriter::write($bundle, 'output.probundle');
|
|
```
|
|
|
|
---
|
|
|
|
## Reading Bundles
|
|
|
|
```php
|
|
use ProPresenter\Parser\ProBundleReader;
|
|
|
|
$bundle = ProBundleReader::read('path/to/presentation.probundle');
|
|
```
|
|
|
|
The reader automatically applies `Zip64Fixer` to handle ProPresenter's broken ZIP64 headers. Works with both PP7-exported bundles and library-generated bundles.
|
|
|
|
### Metadata Access
|
|
|
|
```php
|
|
$bundle->getName(); // Presentation name (from embedded Song)
|
|
$bundle->getProFilename(); // "SongName.pro" (filename inside archive)
|
|
$bundle->getMediaFileCount(); // Number of media files
|
|
```
|
|
|
|
---
|
|
|
|
## Presentation Access
|
|
|
|
```php
|
|
// Get the Song wrapper (same API as ProFileReader)
|
|
$song = $bundle->getSong();
|
|
$song->getName();
|
|
$song->getUuid();
|
|
$song->getGroups();
|
|
$song->getSlides();
|
|
$song->getArrangements();
|
|
|
|
// Get the raw protobuf Presentation
|
|
$presentation = $bundle->getPresentation();
|
|
```
|
|
|
|
The `Song` object returned by `getSong()` has the same API as songs from `ProFileReader::read()`. See [Song API](song.md) for full details.
|
|
|
|
---
|
|
|
|
## Media Files
|
|
|
|
```php
|
|
// All media files: path => raw bytes
|
|
$mediaFiles = $bundle->getMediaFiles();
|
|
foreach ($mediaFiles as $path => $bytes) {
|
|
echo "$path: " . strlen($bytes) . " bytes\n";
|
|
}
|
|
|
|
// Check if a specific media file exists
|
|
if ($bundle->hasMediaFile('/Users/me/Downloads/Media/image.png')) {
|
|
$bytes = $bundle->getMediaFile('/Users/me/Downloads/Media/image.png');
|
|
}
|
|
|
|
// Count
|
|
$bundle->getMediaFileCount(); // 0, 1, 2, ...
|
|
```
|
|
|
|
Media file paths are stored as absolute paths with a leading `/` (matching PP7 export format).
|
|
|
|
---
|
|
|
|
## Creating Bundles
|
|
|
|
Build a `PresentationBundle` from a `Song` and media files:
|
|
|
|
```php
|
|
use ProPresenter\Parser\PresentationBundle;
|
|
use ProPresenter\Parser\ProFileGenerator;
|
|
use ProPresenter\Parser\ProBundleWriter;
|
|
|
|
// Generate a song with a media slide
|
|
$song = ProFileGenerator::generate(
|
|
'My Presentation',
|
|
[
|
|
[
|
|
'name' => 'Background',
|
|
'color' => [0.2, 0.2, 0.2, 1.0],
|
|
'slides' => [
|
|
[
|
|
'media' => 'file:///Users/me/Downloads/Media/background.png',
|
|
'format' => 'png',
|
|
'label' => 'background.png',
|
|
],
|
|
],
|
|
],
|
|
],
|
|
[['name' => 'normal', 'groupNames' => ['Background']]],
|
|
);
|
|
|
|
// Read the media file
|
|
$imageBytes = file_get_contents('/Users/me/Downloads/Media/background.png');
|
|
|
|
// Create the bundle
|
|
$bundle = new PresentationBundle(
|
|
$song,
|
|
'My Presentation.pro',
|
|
['/Users/me/Downloads/Media/background.png' => $imageBytes],
|
|
);
|
|
|
|
// Write to disk
|
|
ProBundleWriter::write($bundle, 'output.probundle');
|
|
```
|
|
|
|
### Media Path Convention
|
|
|
|
Media entries use **absolute paths with a leading `/`**:
|
|
|
|
```php
|
|
$mediaFiles = [
|
|
'/Users/me/Downloads/Media/background.png' => $pngBytes,
|
|
'/Users/me/Downloads/Media/intro.mp4' => $mp4Bytes,
|
|
];
|
|
```
|
|
|
|
This matches PP7's export format. The `file:///` URL in the `.pro` protobuf maps to these paths (strip `file://` prefix).
|
|
|
|
---
|
|
|
|
## Writing Bundles
|
|
|
|
```php
|
|
use ProPresenter\Parser\ProBundleWriter;
|
|
|
|
ProBundleWriter::write($bundle, 'output.probundle');
|
|
```
|
|
|
|
The writer:
|
|
- Creates a standard ZIP archive (deflate compression)
|
|
- Writes media entries first, `.pro` file last
|
|
- Uses atomic write (temp file + rename) for safety
|
|
|
|
---
|
|
|
|
## Round-Trip Example
|
|
|
|
```php
|
|
use ProPresenter\Parser\ProBundleReader;
|
|
use ProPresenter\Parser\ProBundleWriter;
|
|
|
|
// Read
|
|
$bundle = ProBundleReader::read('input.probundle');
|
|
|
|
// Inspect
|
|
echo "Name: " . $bundle->getName() . "\n";
|
|
echo "Media: " . $bundle->getMediaFileCount() . " files\n";
|
|
|
|
// Modify the presentation
|
|
$song = $bundle->getSong();
|
|
$song->setName("Modified Presentation");
|
|
|
|
// Write back
|
|
ProBundleWriter::write($bundle, 'output.probundle');
|
|
```
|
|
|
|
---
|
|
|
|
## Error Handling
|
|
|
|
```php
|
|
try {
|
|
$bundle = ProBundleReader::read('presentation.probundle');
|
|
} catch (\InvalidArgumentException $e) {
|
|
// File not found or empty path
|
|
echo "Error: " . $e->getMessage();
|
|
} catch (\RuntimeException $e) {
|
|
// Empty file, invalid ZIP, no .pro file found, or invalid protobuf
|
|
echo "Error: " . $e->getMessage();
|
|
}
|
|
```
|
|
|
|
### Error Cases
|
|
|
|
| Condition | Exception | Message Pattern |
|
|
|-----------|-----------|-----------------|
|
|
| File not found | `InvalidArgumentException` | `Bundle file not found: ...` |
|
|
| Empty file | `RuntimeException` | `Bundle file is empty: ...` |
|
|
| Invalid ZIP | `RuntimeException` | `Failed to open bundle archive: ...` |
|
|
| No `.pro` entry | `RuntimeException` | `No .pro file found in bundle archive: ...` |
|
|
| Target dir missing (write) | `InvalidArgumentException` | `Target directory does not exist: ...` |
|
|
|
|
---
|
|
|
|
## Key Files
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `php/src/PresentationBundle.php` | Bundle wrapper (Song + media files) |
|
|
| `php/src/ProBundleReader.php` | Reads `.probundle` files (with Zip64Fixer) |
|
|
| `php/src/ProBundleWriter.php` | Writes `.probundle` files (standard ZIP) |
|
|
| `php/src/ProFileGenerator.php` | Generates `.pro` files with media support |
|
|
| `php/src/Zip64Fixer.php` | Fixes ProPresenter ZIP64 header bug |
|
|
| `ref/TestBild.probundle` | Generated reference file (PP7-verified) |
|
|
| `ref/RestBildExportFromPP.probundle` | PP7-exported reference file |
|
|
|
|
---
|
|
|
|
## See Also
|
|
|
|
- [Format Specification](../formats/pp_bundle_spec.md) -- Binary format details
|
|
- [Song API](song.md) -- `.pro` file handling (same Song object inside bundles)
|
|
- [Playlist API](playlist.md) -- `.proplaylist` file handling (similar ZIP pattern)
|