Undo & Redo¶
pycamtasia records project changes as JSON Patch diffs (RFC 6902), giving you memory-efficient undo/redo without full-project snapshots.
Tracking changes¶
Wrap edits in track_changes to record them as a single undoable unit:
from camtasia import load_project
project = load_project("demo.cmproj")
track = project.timeline.tracks[0]
with project.track_changes("add intro clip"):
track.add_video(media.id, start_seconds=0, duration_seconds=5.0)
with project.track_changes("apply drop shadow"):
clip = track.clips[0]
clip.add_drop_shadow()
Each track_changes block captures a before/after diff. If nothing actually
changed inside the block, no history entry is created.
Undo and redo¶
project.undo() # reverts "apply drop shadow"
project.undo() # reverts "add intro clip"
project.redo() # re-applies "add intro clip"
Check what’s available:
project.history.can_undo # True/False
project.history.can_redo # True/False
project.history.descriptions # ["add intro clip", ...]
Persisting history¶
Save history to disk alongside your project so users can undo across sessions:
# Save
history_json = project.history.to_json()
Path("project_history.json").write_text(history_json)
# Restore
from camtasia.history import ChangeHistory
saved = Path("project_history.json").read_text()
project.history = ChangeHistory.from_json(saved)
The @with_undo decorator¶
For functions that always modify a project, use the decorator shorthand:
from camtasia.history import with_undo
@with_undo("normalize audio levels")
def normalize_audio(project):
for track in project.timeline.tracks:
for clip in track.clips:
clip.audio_level = 0.0
Calling normalize_audio(project) automatically wraps the body in
track_changes.
Memory efficiency¶
History stores only the JSON diffs, not full project copies. Check usage with:
project.history.total_patch_size_bytes # e.g. 4096
project.history.undo_count # number of undo steps
The default limit is 100 entries. Configure it when creating a history:
from camtasia.history import ChangeHistory
project.history = ChangeHistory(max_history_depth=50)