BREAKING: Bundle media entries are now flat filenames (no directories). ProBundleWriter flattens all media paths to basename() automatically. ProFileGenerator supports bundleRelative flag for ROOT_CURRENT_RESOURCE URLs, enabling bundles that work on any machine without absolute paths.
9.4 KiB
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
.profile - Top-level Message:
rv.data.Presentation(defined inpresentation.proto) - Proto Definitions: greyshirtguy/ProPresenter7-Proto v7.16.2 (MIT)
- Predecessor: Pro6
.pro6xformat
Container Structure
- Archive Type: Standard ZIP with deflate compression (default)
- ZIP64 EOCD Quirk: ProPresenter 7 exports have the same 98-byte EOCD discrepancy as
.proplaylistfiles - Entry Layout (library output — flat, portable):
- Media files as flat filenames at ZIP root (e.g.,
background.png) - Single
.profile at root (filename only, no directory) - Protobuf uses
ROOT_CURRENT_RESOURCEto resolve media relative to the bundle
- Media files as flat filenames at ZIP root (e.g.,
- Entry Layout (PP7 export — absolute paths):
- Media files at absolute paths with leading
/(e.g.,/Users/me/Downloads/Media/image.png) - Single
.profile at root
- Media files at absolute paths with leading
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
.profile.
2. Archive Structure
Library Output (Flat — Portable)
background.png <-- Media file (flat filename, no directories)
SongName.pro <-- Protobuf-encoded presentation
Media entries use flat filenames only (no directories, no absolute paths). The .pro protobuf references media via ROOT_CURRENT_RESOURCE, which PP7 resolves relative to the bundle. This makes bundles fully portable across machines.
PP7 Export (Absolute Paths)
/Users/me/Downloads/pp-test/Media/background.png <-- Absolute path with leading /
SongName.pro <-- Protobuf-encoded presentation
PP7's own exports use absolute filesystem paths as ZIP entry names. The reader handles both formats.
Entry Order
- Media files first, then the
.profile last - ProPresenter does not enforce order, but this 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
Bundle-Relative (Library Output — Portable)
For bundles, media references use ROOT_CURRENT_RESOURCE with just the filename. PP7 resolves this relative to the bundle itself.
message URL {
string absolute_string = 1; // "background.png" (just the filename)
LocalRelativePath local = 4; // ROOT_CURRENT_RESOURCE + filename
Platform platform = 5; // PLATFORM_MACOS
}
URL.absolute_string = "background.png"
URL.local.root = ROOT_CURRENT_RESOURCE (12)
URL.local.path = "background.png"
URL.platform = PLATFORM_MACOS
Both url and image.file.localUrl use the same structure.
Absolute Paths (PP7 Export / Standalone .pro)
PP7's own exports and standalone .pro files use absolute file:/// URLs with filesystem-based root mappings:
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
LocalRelativePath
message LocalRelativePath {
Root root = 1; // Enum mapping to macOS directory or bundle context
string path = 2; // Relative path from root
}
Root Type Mappings
| Root Enum | Value | macOS Directory |
|---|---|---|
ROOT_UNKNOWN |
0 | Unknown |
ROOT_BOOT_VOLUME |
1 | / (fallback) |
ROOT_USER_HOME |
2 | ~/ |
ROOT_USER_DOCUMENTS |
3 | ~/Documents/ |
ROOT_USER_DOWNLOADS |
4 | ~/Downloads/ |
ROOT_USER_MUSIC |
5 | ~/Music/ |
ROOT_USER_PICTURES |
6 | ~/Pictures/ |
ROOT_USER_VIDEOS |
7 | ~/Movies/ |
ROOT_USER_APP_SUPPORT |
8 | ~/Library/Application Support/ |
ROOT_SHARED |
9 | /Users/Shared/ |
ROOT_SHOW |
10 | ProPresenter library directory |
ROOT_USER_DESKTOP |
11 | ~/Desktop/ |
ROOT_CURRENT_RESOURCE |
12 | Relative to current bundle/document |
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 (library) |
.probundle (PP7 export) |
|---|---|---|---|
| Purpose | Playlist with multiple songs | Single presentation with media | Single presentation with media |
| Compression | Store only (method 0) | Deflate (default) | Deflate |
| Metadata entry | data file (protobuf rv.data.Playlist) |
None (.pro file IS the data) |
None |
| Song entries | Multiple .pro files |
Single .pro file |
Single .pro file |
| Media paths | Absolute minus leading / |
Flat filenames | Absolute with leading / |
| Media URL root | Filesystem-based roots | ROOT_CURRENT_RESOURCE (12) |
Filesystem-based roots |
| ZIP64 | Always ZIP64 | Standard ZIP | ZIP64 |
6. Edge Cases
Bundles Without Media
- Valid. Archive contains only the
.profile. - Use case: Sharing a lyrics-only presentation.
Multiple Media Files
- Valid. Each media file gets its own ZIP entry (flat filename).
- Deduplication: Same filename stored once.
Non-Image Media
- Videos (
.mp4,.mov): Same URL format, differentMetadata.format. - Audio (
.mp3,.wav): Same pattern,MediaType.audiofield used.
Case Sensitivity
.profile 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
ROOT_CURRENT_RESOURCE(12) enables portable bundles: PP7 resolves this root relative to the bundle, so media stored as flat filenames in the ZIP are found on any machineURL.localis required: PP7 cannot find media without theLocalRelativePathinURL.local- Flat filenames work: ZIP entries like
image.png(no directories) withROOT_CURRENT_RESOURCEin the protobuf — PP7 finds the media - Lowercase format: PP7 uses lowercase format strings (
"png"not"PNG") - Standard ZIP is fine: PP7 imports standard deflate-compressed ZIPs without issues
- ZIP64 EOCD bug: PP7 exports have the same 98-byte offset quirk as
.proplaylistfiles - PP7 exports use absolute paths: PP7's own exports use
file:///absolute paths — but these only work on the same machine. The library usesROOT_CURRENT_RESOURCEfor portability instead.
What Didn't Work (Rejected Approaches)
file:///filename.pngwithROOT_BOOT_VOLUME: PP7 cannot resolve bare filenames with filesystem rootsfile:///Users/.../filename.pngwith flat ZIP entry: PP7 needs the URL root to match the ZIP structureROOT_SHOWwith bare filename: PP7 looks in its library dir, not the bundle- Missing
URL.local: PP7 shows "image not found" withoutLocalRelativePath - 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