Skip to content

Marker

A marker on a sequence (or project item).

Backed by the DVAMarker JSON blob; a point marker has no stored end time and reports end == start, like ExtendScript.

Source code in src/py_premiere/models/marker.py
class Marker:
    """A marker on a sequence (or project item).

    Backed by the `DVAMarker` JSON blob; a point marker has no stored end
    time and reports `end == start`, like ExtendScript.
    """

    def __init__(
        self,
        name: str,
        start: Time,
        comments: str = "",
        marker_type: str = "Comment",
        duration: Time | None = None,
    ) -> None:
        _validate_text(name)
        validate_time(start)
        _validate_text(comments)
        _validate_marker_type(marker_type)
        if duration is not None:
            validate_time(duration)
            if duration.ticks < 0:
                # `end` refuses to precede `start`; the constructor must not
                # be a back door to the same state.
                raise ValueError("duration must not be negative")
        blob: dict[str, object] = {
            "mMarkerID": str(uuid.uuid4()),
            "mName": name,
            "mStartTime": {"ticks": start.ticks},
            "mType": marker_type,
        }
        if comments:
            blob["mComment"] = comments
        if duration is not None and duration.ticks:
            blob["mDuration"] = {"ticks": duration.ticks}
        # ObjectID placeholder keeps the attribute first, as Premiere writes
        # it; the owner fills it in when wiring the marker into a document.
        element = ET.Element(
            "Marker",
            {"ObjectID": "", "ClassID": MARKER_CLASS_ID, "Version": "3"},
        )
        element.text = "\n\t\t"
        payload = ET.SubElement(element, "DVAMarker")
        payload.text = json.dumps(
            {"DVAMarker": blob}, sort_keys=True, separators=(",", ":")
        )
        payload.tail = "\n\t"
        self._element = element

    @classmethod
    def _from_xml(cls, _element: ET.Element) -> Marker:
        marker = cls.__new__(cls)
        marker._element = _element
        return marker

    def _blob(self) -> ET.Element:
        element = self._element.find("DVAMarker")
        if element is None:
            raise ValueError("marker has no DVAMarker payload")
        return element

    def _data(self) -> dict[str, Any]:
        data = json.loads(self._blob().text or "{}")
        inner = data.get("DVAMarker")
        return inner if isinstance(inner, dict) else {}

    def _get_str(self, key: str) -> str:
        value = self._data().get(key, "")
        return value if isinstance(value, str) else ""

    def _write(self, key: str, value: object) -> None:
        data = self._data()
        data[key] = value
        # Premiere writes the blob compact with alphabetical keys.
        self._blob().text = json.dumps(
            {"DVAMarker": data}, sort_keys=True, separators=(",", ":")
        )

    @property
    def name(self) -> str:
        """The marker name. Read/write."""
        return self._get_str("mName")

    @name.setter
    def name(self, value: str) -> None:
        _validate_text(value)
        self._write("mName", value)

    @property
    def comments(self) -> str:
        """The marker comments. Read/write."""
        return self._get_str("mComment")

    @comments.setter
    def comments(self, value: str) -> None:
        _validate_text(value)
        self._write("mComment", value)

    @property
    def start(self) -> Time:
        """The marker start time. Read/write."""
        return Time(int(self._data().get("mStartTime", {}).get("ticks", 0)))

    @start.setter
    def start(self, value: Time) -> None:
        validate_time(value)
        self._write("mStartTime", {"ticks": value.ticks})

    @property
    def end(self) -> Time:
        """The marker end time (`start` plus the stored duration). Read/write."""
        data = self._data()
        duration = data.get("mDuration")
        if isinstance(duration, dict) and "ticks" in duration:
            return self.start + Time(int(duration["ticks"]))
        return self.start

    @end.setter
    def end(self, value: Time) -> None:
        validate_time(value)
        if value.ticks < self.start.ticks:
            raise ValueError("end must not precede start")
        self._write("mDuration", {"ticks": value.ticks - self.start.ticks})

    @property
    def type(self) -> str:
        """The marker type (e.g. `Comment`). Read-only."""
        return self._get_str("mType")

    @property
    def guid(self) -> str:
        """The marker's persistent identifier. Read-only."""
        return self._get_str("mMarkerID")

    @property
    def color_index(self) -> int | None:
        """The marker color as a palette index (`0`..`7`). Read/write.

        Uncolored markers report their type's default (Comment green,
        Chapter red, Segmentation magenta, WebLink orange), matching what
        Premiere's `getColorIndex` returns. `None` for a stored color
        outside the palette.
        """
        packed = self._packed_color()
        if packed is None:
            return DEFAULT_INDEX_BY_TYPE.get(self.type, 0)
        return PACKED_TO_INDEX.get(packed)

    @color_index.setter
    def color_index(self, index: int) -> None:
        _validate_color_index(index)
        data = self._data()
        cues = data.get("mCuePointList")
        if not isinstance(cues, list):
            cues = []
        value = json.dumps({"color": MARKER_COLOR_PACKED[index]}, separators=(",", ":"))
        for cue in cues:
            if _cue_color(cue) is not None:
                cue["mValue"] = value
                break
        else:
            # Premiere tags each cue point with a fresh GUID key.
            cues.append({"mKey": f"keywordExtDVAv1_{uuid.uuid4()}", "mValue": value})
        self._write("mCuePointList", cues)

    def _packed_color(self) -> int | None:
        cues = self._data().get("mCuePointList")
        if not isinstance(cues, list):
            return None
        for cue in cues:
            color = _cue_color(cue)
            if color is not None:
                return color
        return None

    @property
    def web_link_url(self) -> str:
        """The URL of a `WebLink` marker. Read-only."""
        return self._get_str("mLocation")

    @property
    def web_link_frame_target(self) -> str:
        """The frame target of a `WebLink` marker. Read-only."""
        return self._get_str("mTarget")

    def set_type_as_comment(self) -> None:
        """Make this a comment marker."""
        self._write("mType", "Comment")

    def set_type_as_chapter(self) -> None:
        """Make this a chapter marker."""
        self._write("mType", "Chapter")

    def set_type_as_segmentation(self) -> None:
        """Make this a segmentation marker."""
        self._write("mType", "Segmentation")

    def set_type_as_web_link(self, url: str, frame_target: str = "") -> None:
        """Make this a web-link marker."""
        _validate_text(url)
        _validate_text(frame_target)
        self._write("mType", "WebLink")
        self._write("mLocation", url)
        self._write("mTarget", frame_target)

    def __repr__(self) -> str:
        return f"Marker(name={self.name!r}, start={self.start.seconds:.3f}s)"

Attributes

color_index property writable

color_index

The marker color as a palette index (0..7). Read/write.

Uncolored markers report their type's default (Comment green, Chapter red, Segmentation magenta, WebLink orange), matching what Premiere's getColorIndex returns. None for a stored color outside the palette.

comments property writable

comments

The marker comments. Read/write.

end property writable

end

The marker end time (start plus the stored duration). Read/write.

guid property

guid

The marker's persistent identifier. Read-only.

name property writable

name

The marker name. Read/write.

start property writable

start

The marker start time. Read/write.

type property

type

The marker type (e.g. Comment). Read-only.

web_link_frame_target

The frame target of a WebLink marker. Read-only.

web_link_url

The URL of a WebLink marker. Read-only.

Methods:

__init__

__init__(
    name,
    start,
    comments="",
    marker_type="Comment",
    duration=None,
)
Source code in src/py_premiere/models/marker.py
def __init__(
    self,
    name: str,
    start: Time,
    comments: str = "",
    marker_type: str = "Comment",
    duration: Time | None = None,
) -> None:
    _validate_text(name)
    validate_time(start)
    _validate_text(comments)
    _validate_marker_type(marker_type)
    if duration is not None:
        validate_time(duration)
        if duration.ticks < 0:
            # `end` refuses to precede `start`; the constructor must not
            # be a back door to the same state.
            raise ValueError("duration must not be negative")
    blob: dict[str, object] = {
        "mMarkerID": str(uuid.uuid4()),
        "mName": name,
        "mStartTime": {"ticks": start.ticks},
        "mType": marker_type,
    }
    if comments:
        blob["mComment"] = comments
    if duration is not None and duration.ticks:
        blob["mDuration"] = {"ticks": duration.ticks}
    # ObjectID placeholder keeps the attribute first, as Premiere writes
    # it; the owner fills it in when wiring the marker into a document.
    element = ET.Element(
        "Marker",
        {"ObjectID": "", "ClassID": MARKER_CLASS_ID, "Version": "3"},
    )
    element.text = "\n\t\t"
    payload = ET.SubElement(element, "DVAMarker")
    payload.text = json.dumps(
        {"DVAMarker": blob}, sort_keys=True, separators=(",", ":")
    )
    payload.tail = "\n\t"
    self._element = element

__repr__

__repr__()
Source code in src/py_premiere/models/marker.py
def __repr__(self) -> str:
    return f"Marker(name={self.name!r}, start={self.start.seconds:.3f}s)"

set_type_as_chapter

set_type_as_chapter()

Make this a chapter marker.

Source code in src/py_premiere/models/marker.py
def set_type_as_chapter(self) -> None:
    """Make this a chapter marker."""
    self._write("mType", "Chapter")

set_type_as_comment

set_type_as_comment()

Make this a comment marker.

Source code in src/py_premiere/models/marker.py
def set_type_as_comment(self) -> None:
    """Make this a comment marker."""
    self._write("mType", "Comment")

set_type_as_segmentation

set_type_as_segmentation()

Make this a segmentation marker.

Source code in src/py_premiere/models/marker.py
def set_type_as_segmentation(self) -> None:
    """Make this a segmentation marker."""
    self._write("mType", "Segmentation")
set_type_as_web_link(url, frame_target='')

Make this a web-link marker.

Source code in src/py_premiere/models/marker.py
def set_type_as_web_link(self, url: str, frame_target: str = "") -> None:
    """Make this a web-link marker."""
    _validate_text(url)
    _validate_text(frame_target)
    self._write("mType", "WebLink")
    self._write("mLocation", url)
    self._write("mTarget", frame_target)