propresenter-php/doc/formats/pp_bundle_spec.md

7.7 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 .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

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

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