Metadata-Version: 2.4
Name: premiere-prproj-reader
Version: 0.1.0
Summary: Read Adobe Premiere Pro project files (.prproj): sequences, tracks, clips, media paths and markers as typed Python data, with an OpenTimelineIO adapter.
Project-URL: Homepage, https://github.com/DylanMerigaud/premiere-prproj-reader
Project-URL: Repository, https://github.com/DylanMerigaud/premiere-prproj-reader
Project-URL: Issues, https://github.com/DylanMerigaud/premiere-prproj-reader/issues
Author: Dylan Merigaud
License-Expression: MIT
License-File: LICENSE
Keywords: adobe,nle,opentimelineio,otio,premiere,premiere-pro,prproj,timeline,video-editing
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Video
Classifier: Typing :: Typed
Requires-Python: >=3.9
Provides-Extra: otio
Requires-Dist: opentimelineio>=0.15; extra == 'otio'
Provides-Extra: test
Requires-Dist: opentimelineio>=0.15; extra == 'test'
Requires-Dist: pytest>=7; extra == 'test'
Description-Content-Type: text/markdown

# premiere-prproj-reader

[![ci](https://github.com/DylanMerigaud/premiere-prproj-reader/actions/workflows/ci.yml/badge.svg)](https://github.com/DylanMerigaud/premiere-prproj-reader/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/premiere-prproj-reader)](https://pypi.org/project/premiere-prproj-reader/)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)

Read Adobe Premiere Pro project files (`.prproj`) without Premiere: sequences, tracks, clips,
in and out points, media paths, markers and the bin tree, as typed Python data. Convert any
sequence to an [OpenTimelineIO](https://opentimelineio.readthedocs.io/) timeline.

It is for developers of tools that need to open a Premiere project: subtitle editors, asset
managers, conform and archive scripts, other editors importing a Premiere edit. Read only, no
runtime dependency, Python 3.9 and later.

## Install

```bash
pip install premiere-prproj-reader
pip install "premiere-prproj-reader[otio]"   # with the OpenTimelineIO adapter
```

## Quick start

The package ships two small projects saved by Premiere Pro 26.3, so this runs as written:

```python
import premiere_prproj_reader as prproj

project = prproj.read(prproj.samples.path("markers"))
print(project.version)  # 45, the project format of Premiere Pro 26.3

for sequence in project.sequences:
    print(sequence.name, sequence.frame_rate, f"{sequence.width}x{sequence.height}")
    for track in sequence.tracks:
        for item in track.items:
            start = prproj.timecode(item.start_ticks, sequence.frame_rate)
            end = prproj.timecode(item.end_ticks, sequence.frame_rate)
            print(" ", track.label, start, end, item.name)
```

```
Seq A 30000/1001 64x36
  V1 00:00:00:00 00:00:04:29 red_64x36.bmp
  A1 00:00:04:29 00:00:05:28 tone_440_1s.wav
Seq B 30000/1001 64x36
  V1 00:00:00:00 00:00:05:28 Seq A
  A1 00:00:00:00 00:00:05:28 Seq A
```

`read()` takes a path, the file's bytes or a binary file object, gzip-compressed or plain XML.

## Media paths (relink, collect, archive)

```python
import premiere_prproj_reader as prproj

project = prproj.read(prproj.samples.path("markers"))
for media in project.media:
    print(media.title, media.path, media.relative_path, "offline" if media.offline else "")
```

`path` is the absolute path Premiere last resolved, `relative_path` the one relative to the project
file. Generated media (Black Video, color mattes, bars and tone) has `generated=True` and no path.

## Clips, source ranges and nested sequences

```python
import premiere_prproj_reader as prproj

project = prproj.read(prproj.samples.path("markers"))
outer = project.find_sequence("Seq B")
item = outer.video_tracks[0].items[0]
print(item.in_point_ticks, item.out_point_ticks, item.speed, item.enabled)
if item.sequence_id is not None:
    nested = project.sequence_by_id(item.sequence_id)
    print("nests", nested.name)
```

Every time is an integer count of Premiere ticks (254,016,000,000 per second), so nothing is lost
to rounding. `ticks_to_seconds`, `ticks_to_frames` and `timecode` convert them; frame rates are
exact `Fraction`s (29.97 is `30000/1001`). `in_point_ticks` and `out_point_ticks` are in the
source's own time, so for a clip at 50 percent speed the source range is half the timeline length.

## Markers

```python
import premiere_prproj_reader as prproj

project = prproj.read(prproj.samples.path("markers"))
for marker in project.find_sequence("Seq A").markers:
    seconds = prproj.ticks_to_seconds(marker.start_ticks)
    print(f"{seconds:.2f}s", marker.type, marker.name, marker.comment, marker.url or "")
```

Sequence markers and clip markers carry name, comment, start, duration, type (Comment, Chapter,
Segmentation, WebLink, FlashCuePoint) and the URL of a web link.

## The bin tree

```python
import premiere_prproj_reader as prproj

project = prproj.read(prproj.samples.path("markers"))
for entry in project.root.walk():
    kind = "bin" if isinstance(entry, prproj.Bin) else "clip"
    print(kind, entry.name)
```

## OpenTimelineIO

With the `otio` extra installed:

<!-- harness: skip -->
```python
import opentimelineio as otio
import premiere_prproj_reader as prproj
from premiere_prproj_reader.otio import to_timeline

project = prproj.read(prproj.samples.path("markers"))
timeline = to_timeline(project.find_sequence("Seq A"), project)
print(otio.adapters.write_to_string(timeline, "otio_json")[:80])
```

Installing the `otio` extra also registers a `prproj` adapter, so OpenTimelineIO tools read
projects directly:

<!-- harness: skip -->
```python
import opentimelineio as otio

timeline = otio.adapters.read_from_file("edit.prproj")
```

From there, OpenTimelineIO and its adapter plugins write the timeline to the other formats they
support (Final Cut Pro XML, CMX 3600 EDL and more).

## Command line

```bash
prproj info edit.prproj          # versions, sequences, tracks
prproj json edit.prproj          # the whole project as JSON
prproj otio edit.prproj edit.otio --sequence "Main edit"
```

## From another language (C#, Rust, JavaScript)

Editors and subtitle tools written in other languages can call the CLI and read its JSON instead of
keeping their own `.prproj` parser:

```bash
prproj json edit.prproj > edit.json
```

```json
{"version": 45, "build": "26.3.0x93 - 20/07/2026 14:03:26",
 "sequences": [{"id": "9c19c828-...", "name": "Seq A", "frame_rate": "30000/1001",
   "width": 64, "height": 36,
   "video_tracks": [{"kind": "video", "index": 0, "muted": false, "locked": false,
     "items": [{"name": "red_64x36.bmp", "start_ticks": 0, "end_ticks": 1262874412800,
       "in_point_ticks": 914456685542400, "out_point_ticks": 915719559955200,
       "speed": 1.0, "enabled": true, "sequence_id": null, "markers": [],
       "media": {"title": "red_64x36.bmp", "path": "C:\\...\\red_64x36.bmp",
                 "relative_path": "..\\assets\\red_64x36.bmp", "offline": false,
                 "generated": false}}]}],
   "audio_tracks": [], "markers": [{"name": "chapter", "type": "Chapter",
     "start_ticks": 254016000000, "duration_ticks": 0, "comment": "", "url": null}]}],
 "media": [], "root": {"name": "", "children": []}}
```

(Shortened.) Ticks divide by 254,016,000,000 for seconds. A subtitle editor that needs the marker
list, or an editor importing a Premiere timeline, maps `sequences[].markers` and
`sequences[].video_tracks[].items[]` directly; an editor that already reads OpenTimelineIO can use
`prproj otio edit.prproj edit.otio` instead.

## Which Premiere versions

The tests read projects saved by Premiere Pro from 2.0 (project format 18, 2006) to 26.3 (format
45, 2026): format 18, 19 (CS3), 35 (11.1), 38 (14.0), 42 (24.4), 43 (25.2) and 45 (26.3). The
element names this reader follows are the same across all of them. Older formats are skeletons
reduced from real projects by `scripts/reduce_fixture.py` (every name and path replaced); the
format 45 projects are the py-premiere sample set (MIT), and the test suite checks this library
against the values Premiere's own ExtendScript reported for them (`tests/fixtures/truth/`).

If a project of yours does not read, `python scripts/drift_check.py DIR` reports what failed, and
`scripts/reduce_fixture.py` makes a shareable copy for a bug report.

## Why this one

| | premiere-prproj-reader | [py-premiere](https://github.com/Pouf/py-premiere) | [prproj-parser](https://github.com/catastrofia/prproj-parser) | [otio-premiereproject](https://github.com/splidje/otio-premiereproject) | [prproj-rs](https://github.com/supercuts/prproj-rs) |
|---|---|---|---|---|---|
| Scope | read only, plain data | read and write, ExtendScript-shaped object model | read only | OpenTimelineIO adapter | Rust parser |
| Formats tested | 18 to 45 (Premiere Pro 2.0 to 26.3) | Premiere Pro 26.3 samples | 2023 to 2026, per its README | not stated | one project in `test_files/` |
| Checked against Premiere's ExtendScript values | yes (`tests/fixtures/truth/`) | yes, per its README | not stated | not stated | not stated |
| OpenTimelineIO | `to_timeline` and an adapter plugin | no | no | yes | no |
| Markers, bin tree, source in and out points | yes | yes | not in its API table | in and out points only | no |
| Last change | 2026 | 2026 | 2026 | 2021 | 2023 |

The other columns come from each project's README and source as read on 2026-11-02.

Pick py-premiere when you need to write projects or read effects, keyframes and masks. Pick this
one when you want a small dependency-free reader whose output is plain frozen dataclasses, exact
ticks, and an OpenTimelineIO timeline, tested across twenty years of Premiere versions.

## Safety

Projects come from users. The reader refuses XML carrying a DOCTYPE or ENTITY declaration
(including one hidden by a UTF-16 or UTF-32 encoding), caps the decompressed size
(`read(..., max_bytes=...)`, 1 GiB by default) and bin nesting, and raises `PrprojError` on
malformed numbers or references instead of crashing with another exception.

## License

MIT. The sample projects in `src/premiere_prproj_reader/samples/` and `tests/fixtures/v45_*` come from
py-premiere (MIT, Copyright (c) 2026 Aurore Delaunay); its licence is kept beside them.
