Kdenlive Automation
Python packages for automating Kdenlive video editor via JSON-RPC
Installation
npx kdenlive-automationAsk AI about Kdenlive Automation
Powered by Claude · Grounded in docs
I know everything about Kdenlive Automation. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
Kdenlive Automation
Python packages for automating Kdenlive video editor via JSON-RPC over WebSocket.
Packages
| Package | Description | PyPI |
|---|---|---|
| kdenlive-api | Python client library for programmatic control of Kdenlive | |
| kdenlive-mcp | MCP server for AI assistant integration (Claude, etc.) |
Requirements
- Python 3.11+
- Kdenlive with WebSocket server enabled (built from
IO-AtelierTech/kdenlive) - uv for package management (recommended)
Installation
From PyPI
# Client library only
pip install kdenlive-api
# MCP server (includes kdenlive-api)
pip install kdenlive-mcp
From Source
git clone https://github.com/IO-AtelierTech/kdenlive-automation.git
cd kdenlive-automation
# Install both packages in development mode
uv sync
Quick Start
Using kdenlive-api
import asyncio
from kdenlive_api import KdenliveClient
async def main():
async with KdenliveClient() as kdenlive:
# Get project info
info = await kdenlive.project.get_info()
print(f"Project: {info.name} ({info.width}x{info.height} @ {info.fps}fps)")
# Import media files
clip_ids = await kdenlive.bin.import_clips([
"/path/to/video1.mp4",
"/path/to/video2.mp4"
])
# Add clip to timeline
timeline_clip_id = await kdenlive.timeline.insert_clip(
bin_clip_id=clip_ids[0],
track_id=1,
position=0
)
# Apply an effect
await kdenlive.effects.add(
effect_id="brightness",
clip_id=timeline_clip_id
)
# Set effect property
await kdenlive.effects.set_property(
clip_id=timeline_clip_id,
effect_id="brightness",
property="level",
value=1.2
)
# Render the project
job = await kdenlive.render.start(
preset_name="YouTube 1080p",
output_path="/path/to/output.mp4"
)
# Monitor render progress
async for progress in kdenlive.render.watch(job.job_id):
print(f"Rendering: {progress.progress}%")
print("Done!")
asyncio.run(main())
Using kdenlive-mcp with Claude
Start the MCP server:
kdenlive-mcp
Or run directly:
uv run kdenlive-mcp
See MCP Configuration Guide for Claude Desktop setup.
API Namespaces
project
Project lifecycle management.
await kdenlive.project.open("/path/to/project.kdenlive")
await kdenlive.project.save()
await kdenlive.project.close(save_changes=True)
await kdenlive.project.new(profile="atsc_1080p_25")
await kdenlive.project.undo()
await kdenlive.project.redo()
info = await kdenlive.project.get_info()
timeline
Timeline operations and clip manipulation.
# Get timeline state
info = await kdenlive.timeline.get_info()
tracks = await kdenlive.timeline.get_tracks()
clips = await kdenlive.timeline.get_clips(track_id=1)
position = await kdenlive.timeline.get_position()
# Manipulate clips
clip_id = await kdenlive.timeline.insert_clip(bin_clip_id, track_id, position)
await kdenlive.timeline.move_clip(clip_id, new_track_id, new_position)
await kdenlive.timeline.resize_clip(clip_id, in_point=10, out_point=200)
new_clips = await kdenlive.timeline.split_clip(clip_id, position=125)
await kdenlive.timeline.delete_clip(clip_id)
# Track management
track_id = await kdenlive.timeline.add_track("video", name="VFX")
await kdenlive.timeline.set_track_property(track_id, "locked", True)
await kdenlive.timeline.delete_track(track_id)
# Navigation
await kdenlive.timeline.seek(1500)
await kdenlive.timeline.set_selection([clip_id1, clip_id2])
bin
Media bin and clip management.
# List contents
clips = await kdenlive.bin.list_clips(folder_id="folder1")
folders = await kdenlive.bin.list_folders()
clip_info = await kdenlive.bin.get_clip_info(clip_id)
# Import media
clip_id = await kdenlive.bin.import_clip("/path/to/video.mp4")
clip_ids = await kdenlive.bin.import_clips(["/path/to/a.mp4", "/path/to/b.mp4"])
# Organize
folder_id = await kdenlive.bin.create_folder("B-Roll", parent_id="folder1")
await kdenlive.bin.rename_item(clip_id, "New Name")
await kdenlive.bin.move_item(clip_id, target_folder_id="folder2")
await kdenlive.bin.delete_clip(clip_id)
await kdenlive.bin.delete_folder(folder_id)
# Markers
markers = await kdenlive.bin.get_clip_markers(clip_id)
marker_id = await kdenlive.bin.add_clip_marker(clip_id, position=100, comment="Good take")
await kdenlive.bin.delete_clip_marker(clip_id, marker_id)
effects
Effect application and keyframe animation.
# Browse effects
effects = await kdenlive.effects.list_available()
effect_info = await kdenlive.effects.get_info("brightness")
# Apply effects
effect_id = await kdenlive.effects.add("brightness", clip_id)
clip_effects = await kdenlive.effects.get_clip_effects(clip_id)
await kdenlive.effects.enable(clip_id, effect_id)
await kdenlive.effects.disable(clip_id, effect_id)
await kdenlive.effects.remove(clip_id, effect_id)
# Properties
value = await kdenlive.effects.get_property(clip_id, effect_id, "level")
await kdenlive.effects.set_property(clip_id, effect_id, "level", 1.5)
# Keyframes
keyframes = await kdenlive.effects.get_keyframes(clip_id, effect_id, "level")
await kdenlive.effects.set_keyframe(clip_id, effect_id, "level", position=50, value=1.2)
await kdenlive.effects.delete_keyframe(clip_id, effect_id, "level", position=50)
# Batch operations
await kdenlive.effects.copy_to_clips(source_clip_id, effect_id, [target1, target2])
await kdenlive.effects.reorder(clip_id, effect_id, new_index=0)
render
Render job management.
# Presets
presets = await kdenlive.render.get_presets()
preset_info = await kdenlive.render.get_preset_info("YouTube 1080p")
# Render
job = await kdenlive.render.start("YouTube 1080p", "/output/video.mp4")
jobs = await kdenlive.render.start_with_guides("YouTube 1080p", "/output/")
# Monitor
status = await kdenlive.render.get_status(job.job_id)
active = await kdenlive.render.get_active_job()
all_jobs = await kdenlive.render.get_jobs()
# Control
await kdenlive.render.stop(job.job_id)
await kdenlive.render.stop_all()
# Stream progress
async for progress in kdenlive.render.watch(job.job_id):
print(f"{progress.progress}% - Frame {progress.frame}/{progress.total_frames}")
asset
Asset discovery and favorites.
categories = await kdenlive.asset.list_categories()
results = await kdenlive.asset.search("blur")
effects = await kdenlive.asset.get_effects_by_category("video")
# Favorites
favorites = await kdenlive.asset.get_favorites()
await kdenlive.asset.add_favorite("brightness")
await kdenlive.asset.remove_favorite("brightness")
# Effect presets
presets = await kdenlive.asset.get_presets("brightness")
await kdenlive.asset.save_preset("brightness", "My Preset")
await kdenlive.asset.delete_preset("brightness", "My Preset")
transition
Transition management between clips.
types = await kdenlive.transition.list()
transition = await kdenlive.transition.add("dissolve", from_clip_id, to_clip_id)
props = await kdenlive.transition.get_properties(transition.id)
await kdenlive.transition.set_property(transition.id, "duration", 25)
await kdenlive.transition.remove(transition.id)
composition
Composition (track blending) operations.
compositions = await kdenlive.composition.list()
comp = await kdenlive.composition.add("qtblend", track_id=2, position=0)
props = await kdenlive.composition.get_properties(comp.id)
await kdenlive.composition.set_property(comp.id, "opacity", 80)
await kdenlive.composition.remove(comp.id)
Mock Mode
Develop and test without a running Kdenlive instance:
# Enable mock mode
client = KdenliveClient(mock=True)
async with client:
# Returns mock data
info = await client.project.get_info()
print(info.name) # "Mock Project"
Connection Options
client = KdenliveClient(
url="ws://localhost:9876", # WebSocket URL
mock=False, # Use mock mode
reconnect=True, # Auto-reconnect on disconnect
reconnect_delay=1.0, # Seconds between reconnect attempts
)
Error Handling
from kdenlive_api.exceptions import (
KdenliveError,
ConnectionError,
ProjectNotOpenError,
ClipNotFoundError,
TrackNotFoundError,
EffectNotFoundError,
RenderInProgressError,
)
try:
await kdenlive.timeline.delete_clip(999)
except ClipNotFoundError:
print("Clip does not exist")
except KdenliveError as e:
print(f"Kdenlive error: {e}")
Documentation
- API Reference - Complete API documentation
- MCP Configuration - Setting up with Claude Desktop
- Interface Contract - JSON-RPC protocol specification
Development
# Install dev dependencies
uv sync --all-extras
# Fetch latest RPC schema
./scripts/fetch-schema.sh
# Run linting
uv run ruff check packages/
# Run formatting
uv run ruff format packages/
# Run tests
uv run pytest
# Build packages
uv build packages/kdenlive-api
uv build packages/kdenlive-mcp
Contributing
Contributions are welcome! Please see the Contributing Guide for details.
License
MIT License - see LICENSE for details.
Related
- Kdenlive - Free and open source video editor
- IO-AtelierTech/kdenlive - Kdenlive fork with WebSocket RPC support
- MCP - Model Context Protocol
