207 lines
7.7 KiB
Markdown
207 lines
7.7 KiB
Markdown
# ProPresenter 7 `.probundle` File Format Specification
|
|
|
|
**Version:** 1.0
|
|
**Target Audience:** AI agents, automated parsers, developers
|
|
**Proto Source:** greyshirtguy/ProPresenter7-Proto v7.16.2 (MIT License)
|
|
|
|
---
|
|
|
|
## 1. Overview
|
|
|
|
### File Format
|
|
- **Extension:** `.probundle`
|
|
- **Container Format:** Standard ZIP archive (PKZIP 2.0+, default deflate compression)
|
|
- **Binary Format:** Protocol Buffers (Google protobuf v3) for the embedded `.pro` file
|
|
- **Top-level Message:** `rv.data.Presentation` (defined in `presentation.proto`)
|
|
- **Proto Definitions:** greyshirtguy/ProPresenter7-Proto v7.16.2 (MIT)
|
|
- **Predecessor:** Pro6 `.pro6x` format
|
|
|
|
### Container Structure
|
|
- **Archive Type:** Standard ZIP with deflate compression (default)
|
|
- **ZIP64 EOCD Quirk:** ProPresenter 7 exports have the same 98-byte EOCD discrepancy as `.proplaylist` files
|
|
- **Entry Layout:**
|
|
- Media files at **absolute paths with leading `/`** (e.g., `/Users/me/Downloads/Media/image.png`)
|
|
- Single `.pro` file at root (filename only, no directory)
|
|
|
|
### Purpose
|
|
A `.probundle` packages a single ProPresenter presentation (`.pro` file) together with all its referenced media assets (images, videos, audio) into a single portable archive. This enables sharing presentations between machines without losing media references.
|
|
|
|
### File Validity
|
|
- **Empty files (0 bytes):** Invalid. Throw exception.
|
|
- **Archives without `.pro`:** Invalid. Throw exception.
|
|
- **Bundles without media:** Valid. A presentation with no media actions produces a ZIP containing only the `.pro` file.
|
|
|
|
---
|
|
|
|
## 2. Archive Structure
|
|
|
|
### Entry Layout
|
|
|
|
```
|
|
/Users/me/Downloads/pp-test/Media/background.png <-- Media file (absolute path with leading /)
|
|
SongName.pro <-- Protobuf-encoded presentation
|
|
```
|
|
|
|
### Entry Order
|
|
- **Media files first**, then the `.pro` file last
|
|
- ProPresenter does not enforce order, but this matches PP7 export behavior
|
|
|
|
### Media Entry Naming
|
|
- Media entries use **absolute filesystem paths with a leading `/`**
|
|
- Standard unzip tools strip the leading `/` with a warning: `warning: stripped absolute path spec from /Users/.../image.png`
|
|
- This is intentional and matches PP7 export behavior
|
|
|
|
### Compression
|
|
- **ProPresenter exports:** Standard deflate compression
|
|
- **Writer output:** Standard deflate compression (ZipArchive defaults)
|
|
- **No special attributes needed:** Standard permissions, no forced store compression
|
|
|
|
---
|
|
|
|
## 3. Protobuf Content (`.pro` File)
|
|
|
|
### Media URL Format
|
|
|
|
Media references in the `.pro` protobuf must use `file:///` absolute URLs with a `LocalRelativePath` in `URL.local`.
|
|
|
|
#### URL Structure
|
|
|
|
```protobuf
|
|
message URL {
|
|
string absolute_string = 1; // "file:///Users/me/Downloads/Media/image.png"
|
|
LocalRelativePath local = 4; // Root + relative path
|
|
Platform platform = 5; // PLATFORM_MACOS
|
|
}
|
|
```
|
|
|
|
#### LocalRelativePath
|
|
|
|
```protobuf
|
|
message LocalRelativePath {
|
|
Root root = 1; // Enum mapping to macOS directory
|
|
string path = 2; // Relative path from root directory
|
|
}
|
|
```
|
|
|
|
### Root Type Mappings
|
|
|
|
| Root Enum | Value | macOS Directory |
|
|
|-----------|-------|-----------------|
|
|
| `ROOT_BOOT_VOLUME` | 0 | `/` (fallback) |
|
|
| `ROOT_USER_HOME` | 2 | `~/` |
|
|
| `ROOT_USER_DOWNLOADS` | 4 | `~/Downloads/` |
|
|
| `ROOT_USER_DOCUMENTS` | 5 | `~/Documents/` |
|
|
| `ROOT_USER_DESKTOP` | 6 | `~/Desktop/` |
|
|
| `ROOT_USER_MUSIC` | 7 | `~/Music/` |
|
|
| `ROOT_USER_PICTURES` | 8 | `~/Pictures/` |
|
|
| `ROOT_USER_VIDEOS` | 9 | `~/Movies/` |
|
|
| `ROOT_SHOW` | 10 | ProPresenter library directory |
|
|
|
|
### Path Construction Example
|
|
|
|
For media at `file:///Users/thorsten/Downloads/pp-test/Media/background.png`:
|
|
|
|
```
|
|
URL.absolute_string = "file:///Users/thorsten/Downloads/pp-test/Media/background.png"
|
|
URL.local.root = ROOT_USER_DOWNLOADS (4)
|
|
URL.local.path = "pp-test/Media/background.png"
|
|
URL.platform = PLATFORM_MACOS
|
|
```
|
|
|
|
### Media Metadata
|
|
|
|
| Field | Expected Value | Notes |
|
|
|-------|---------------|-------|
|
|
| `Metadata.format` | Lowercase: `"png"`, `"jpg"`, `"mp4"` | PP7 uses lowercase |
|
|
| `Action.type` | `ACTION_TYPE_MEDIA` | Media action type |
|
|
| `MediaType.layer_type` | `LAYER_TYPE_FOREGROUND` | Default for slide media |
|
|
|
|
---
|
|
|
|
## 4. ZIP64 EOCD Quirk
|
|
|
|
### Issue
|
|
ProPresenter 7 exports `.probundle` files with the same ZIP64 EOCD bug as `.proplaylist` files: a 98-byte discrepancy between the ZIP64 EOCD locator offset and the actual EOCD position.
|
|
|
|
### Workaround
|
|
The reader applies `Zip64Fixer` before opening the archive. This searches backward from the end of file for the ZIP64 EOCD signature (`0x06064b50`) and corrects the offset.
|
|
|
|
### Writer Behavior
|
|
The writer produces standard ZIPs without the bug. PHP's `ZipArchive` creates clean archives that PP7 imports without issues.
|
|
|
|
---
|
|
|
|
## 5. Differences from `.proplaylist`
|
|
|
|
| Aspect | `.proplaylist` | `.probundle` |
|
|
|--------|---------------|-------------|
|
|
| **Purpose** | Playlist with multiple songs | Single presentation with media |
|
|
| **Compression** | Store only (method 0) | Deflate (default) |
|
|
| **Metadata entry** | `data` file (protobuf `rv.data.Playlist`) | None (`.pro` file IS the data) |
|
|
| **Song entries** | Multiple `.pro` files | Single `.pro` file |
|
|
| **Media paths** | Absolute minus leading `/` | Absolute **with** leading `/` |
|
|
| **ZIP64** | Always ZIP64 | Standard ZIP (PP7 exports as ZIP64) |
|
|
|
|
---
|
|
|
|
## 6. Edge Cases
|
|
|
|
### Bundles Without Media
|
|
- **Valid.** Archive contains only the `.pro` file.
|
|
- **Use case:** Sharing a lyrics-only presentation.
|
|
|
|
### Multiple Media Files
|
|
- **Valid.** Each media file gets its own ZIP entry at its absolute path.
|
|
- **Deduplication:** Same path stored once.
|
|
|
|
### Non-Image Media
|
|
- **Videos** (`.mp4`, `.mov`): Same URL format, different `Metadata.format`.
|
|
- **Audio** (`.mp3`, `.wav`): Same pattern, `MediaType.audio` field used.
|
|
|
|
### Case Sensitivity
|
|
- `.pro` file detection is case-insensitive (`.pro`, `.Pro`, `.PRO`).
|
|
- Media format strings should be **lowercase** to match PP7 behavior.
|
|
|
|
---
|
|
|
|
## 7. Reverse-Engineering Evidence
|
|
|
|
### Reference Files
|
|
- **TestBild.probundle:** Generated by this library, verified importable by PP7 with image found
|
|
- **RestBildExportFromPP.probundle:** Exported by PP7 after import, used as comparison reference
|
|
|
|
### Key Discoveries
|
|
1. **Absolute paths with leading `/`:** PP7 stores media at full absolute filesystem paths in the ZIP, including the leading `/`
|
|
2. **`URL.local` is required:** PP7 cannot find media without the `LocalRelativePath` in `URL.local`
|
|
3. **`file:///` prefix required:** `URL.absolute_string` must use the `file:///` protocol prefix
|
|
4. **Lowercase format:** PP7 uses lowercase format strings (`"png"` not `"PNG"`)
|
|
5. **Standard ZIP is fine:** PP7 imports standard deflate-compressed ZIPs without issues — the ZIP64/store/permission quirks in PP7 exports are artifacts, not requirements
|
|
6. **ZIP64 EOCD bug:** PP7 exports have the same 98-byte offset quirk as `.proplaylist` files
|
|
|
|
### What Didn't Work (Rejected Approaches)
|
|
- **Relative media paths:** PP7 cannot resolve `Media/image.png` — needs absolute paths
|
|
- **Missing `URL.local`:** PP7 shows "image not found" without `LocalRelativePath`
|
|
- **Missing `file:///`:** Plain paths like `/Users/me/image.png` are not recognized
|
|
- **Uppercase format:** `"PNG"` works but doesn't match PP7's own output
|
|
- **Forced store compression / 000 permissions:** Unnecessary hacks that don't affect import
|
|
|
|
---
|
|
|
|
## Appendix: PP7 Export vs Library Output
|
|
|
|
### PP7 Export Characteristics (informational only)
|
|
- ZIP64 format with 98-byte EOCD offset bug
|
|
- Store compression (method 0)
|
|
- File permissions set to `0000`
|
|
- These are PP7 artifacts — the library reader handles them, the writer doesn't reproduce them
|
|
|
|
### Library Output Characteristics
|
|
- Standard ZIP (PKZIP 2.0+)
|
|
- Deflate compression (ZipArchive default)
|
|
- Normal file permissions
|
|
- PP7 imports these without issues
|
|
|
|
---
|
|
|
|
**End of Specification**
|