# 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**