Python client library for Kdenlive JSON-RPC API
Project description
kdenlive-api
Python client library for the Kdenlive JSON-RPC WebSocket API.
Installation
pip install kdenlive-api
Or with uv:
uv add kdenlive-api
Requirements
- Python 3.11+
- Kdenlive with RPC server enabled (requires kdenlive-websocket fork)
Quick Start
import asyncio
from kdenlive_api import KdenliveClient
async def main():
async with KdenliveClient() as client:
# Get project info
info = await client.project.get_info()
print(f"Project: {info.name}")
print(f"Resolution: {info.width}x{info.height}")
print(f"FPS: {info.fps}")
# List clips in bin
clips = await client.bin.list_clips()
for clip in clips:
print(f" - {clip.name}")
# Get timeline info
timeline = await client.timeline.get_info()
print(f"Timeline duration: {timeline.duration} frames")
asyncio.run(main())
Examples
The examples/ directory contains 15 ready-to-use scripts solving common video editing problems:
| Script | Problem Solved |
|---|---|
| 01_batch_import_organize.py | Import and auto-organize files by type |
| 02_quick_rough_cut.py | Add clips to timeline in order |
| 03_auto_transitions.py | Add transitions between all clips |
| 04_batch_color_grade.py | Apply consistent color correction |
| 05_add_fade_effects.py | Add fade-in/fade-out effects |
| 06_trim_silence.py | Trim dead air from clips |
| 07_track_setup.py | Create organized track structure |
| 08_multi_format_export.py | Export for multiple platforms |
| 09_scene_splitter.py | Split clips at intervals |
| 10_cleanup_timeline.py | Remove tiny leftover clips |
| 11_backup_project.py | Auto-save with timestamps |
| 12_apply_preset_effects.py | Apply saved effect presets |
| 13_audio_ducking_setup.py | Lower music during speech |
| 14_marker_based_export.py | Export by guide markers |
| 15_project_template.py | Create projects from templates |
All scripts accept command-line arguments. Run with --help for usage:
python examples/01_batch_import_organize.py --help
Configuration
Environment Variables
| Variable | Description | Default |
|---|---|---|
KDENLIVE_WS_URL |
WebSocket URL | ws://localhost:9876 |
KDENLIVE_AUTH_TOKEN |
Bearer token for authentication | None |
Programmatic Configuration
from kdenlive_api import KdenliveClient
# Custom URL
client = KdenliveClient(url="ws://192.168.1.100:9876")
# With authentication
client = KdenliveClient(auth_token="your-secret-token")
# Both
client = KdenliveClient(
url="ws://192.168.1.100:9876",
auth_token="your-secret-token"
)
API Namespaces
The client provides access to Kdenlive functionality through typed API namespaces:
Project (client.project)
# Get project information
info = await client.project.get_info()
# Open a project
await client.project.open("/path/to/project.kdenlive")
# Save project
await client.project.save()
await client.project.save("/path/to/new-project.kdenlive")
# Create new project
await client.project.new(profile="atsc_1080p_25")
# Close project
await client.project.close(save_changes=True)
# Undo/Redo
await client.project.undo()
await client.project.redo()
Timeline (client.timeline)
# Get timeline info
info = await client.timeline.get_info()
# Get all tracks
tracks = await client.timeline.get_tracks()
# Get clips (all or by track)
clips = await client.timeline.get_clips()
clips = await client.timeline.get_clips(track_id=0)
# Insert clip from bin
clip_id = await client.timeline.insert_clip(
bin_clip_id="abc123",
track_id=0,
position=100 # frames
)
# Move clip
await client.timeline.move_clip(clip_id=1, track_id=1, position=200)
# Delete clip
await client.timeline.delete_clip(clip_id=1)
# Split clip
parts = await client.timeline.split_clip(clip_id=1, position=50)
# Resize/trim clip
await client.timeline.resize_clip(clip_id=1, in_point=10, out_point=100)
# Track management
track_id = await client.timeline.add_track("video", name="V3")
await client.timeline.delete_track(track_id=2)
# Playhead
await client.timeline.seek(position=500)
position = await client.timeline.get_position()
Bin (client.bin)
# List clips and folders
clips = await client.bin.list_clips()
clips = await client.bin.list_clips(folder_id="folder123")
folders = await client.bin.list_folders()
# Get clip details
info = await client.bin.get_clip_info(clip_id="abc123")
# Import media
clip_id = await client.bin.import_clip("/path/to/video.mp4")
clip_ids = await client.bin.import_clips([
"/path/to/video1.mp4",
"/path/to/video2.mp4"
], folder_id="folder123")
# Organize
folder_id = await client.bin.create_folder("Footage", parent_id=None)
await client.bin.rename_item(item_id="abc123", name="New Name")
await client.bin.move_item(item_id="abc123", target_folder_id="folder123")
await client.bin.delete_clip(clip_id="abc123")
# Markers
marker_id = await client.bin.add_clip_marker(
clip_id="abc123",
position=100,
comment="Important moment"
)
await client.bin.delete_clip_marker(clip_id="abc123", marker_id=0)
Effects (client.effects)
# List available effects
effects = await client.effects.list_available()
# Get effect details
info = await client.effects.get_info(effect_id="blur")
# Apply effect to clip
instance_id = await client.effects.add(effect_id="blur", clip_id=1)
# Get effects on a clip
clip_effects = await client.effects.get_clip_effects(clip_id=1)
# Modify effect parameters
await client.effects.set_property(
clip_id=1,
effect_id="effect123",
property_name="radius",
value=10.5
)
# Enable/disable effects
await client.effects.enable(clip_id=1, effect_id="effect123")
await client.effects.disable(clip_id=1, effect_id="effect123")
# Remove effect
await client.effects.remove(clip_id=1, effect_id="effect123")
# Keyframes
keyframes = await client.effects.get_keyframes(
clip_id=1,
effect_id="effect123",
property_name="radius"
)
await client.effects.set_keyframe(
clip_id=1,
effect_id="effect123",
property_name="radius",
position=50,
value=20.0
)
await client.effects.delete_keyframe(
clip_id=1,
effect_id="effect123",
property_name="radius",
position=50
)
Render (client.render)
# Get available presets
presets = await client.render.get_presets()
# Start render
job = await client.render.start(
preset_name="MP4-H264",
output_path="/path/to/output.mp4"
)
# Monitor progress
status = await client.render.get_status(job_id=job.job_id)
print(f"Progress: {status.progress}%")
# Get all jobs
jobs = await client.render.get_jobs()
active = await client.render.get_active_job()
# Stop render
await client.render.stop(job_id=job.job_id)
Assets (client.asset)
# Browse categories
categories = await client.asset.list_categories()
# Search
results = await client.asset.search("blur")
# Get effects by category
effects = await client.asset.get_effects_by_category("Color")
# Favorites
favorites = await client.asset.get_favorites()
await client.asset.add_favorite(asset_id="blur")
await client.asset.remove_favorite(asset_id="blur")
# Presets
presets = await client.asset.get_presets(effect_id="blur")
await client.asset.save_preset(effect_id="blur", preset_name="My Blur")
Transitions (client.transition)
# List available transitions
transitions = await client.transition.list()
# Add transition between clips
info = await client.transition.add(
transition_type="dissolve",
from_clip_id=1,
to_clip_id=2
)
# Remove transition
await client.transition.remove(transition_id=info.id)
Compositions (client.composition)
# List compositions
compositions = await client.composition.list()
# Add composition
info = await client.composition.add(
composition_type="composite",
track=0,
position=100
)
# Remove composition
await client.composition.remove(composition_id=info.id)
Notifications
Subscribe to real-time notifications from Kdenlive:
async def handle_notification(method: str, params: dict):
if method == "render.progress":
print(f"Render progress: {params['progress']}%")
elif method == "timeline.changed":
print("Timeline was modified")
client = KdenliveClient()
client.on_notification(handle_notification)
await client.connect()
Error Handling
from kdenlive_api import (
KdenliveError,
ConnectionError,
ProjectNotOpenError,
ClipNotFoundError,
ValidationError,
)
try:
await client.project.get_info()
except ConnectionError:
print("Cannot connect to Kdenlive")
except ProjectNotOpenError:
print("No project is open")
except ClipNotFoundError as e:
print(f"Clip not found: {e}")
except ValidationError as e:
print(f"Invalid parameters: {e}")
except KdenliveError as e:
print(f"Kdenlive error: {e}")
Type Safety
All API responses are typed using Pydantic models:
from kdenlive_api import ProjectInfo, ClipInfo, TimelineClip
info: ProjectInfo = await client.project.get_info()
print(info.width) # IDE autocomplete works!
License
MIT License - see LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kdenlive_api-0.2.0.tar.gz.
File metadata
- Download URL: kdenlive_api-0.2.0.tar.gz
- Upload date:
- Size: 18.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
db684ad827b7f9423da8fefe2b2643803e5a83cc0bffa9fb8861955e4146980a
|
|
| MD5 |
d3c7e178a189ad4a31aa9a55b2ed2cd0
|
|
| BLAKE2b-256 |
d76e8fd1e4247c4640c2c1c49cb564f5d8ba99d643779815336071cf973a7c39
|
File details
Details for the file kdenlive_api-0.2.0-py3-none-any.whl.
File metadata
- Download URL: kdenlive_api-0.2.0-py3-none-any.whl
- Upload date:
- Size: 24.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5c3efdd5b36babc092d0b14c91ed92f9e88a57c21f429a086dab97fc1d05cff
|
|
| MD5 |
bc19185b08459454d52b066bda25743a
|
|
| BLAKE2b-256 |
1d3b975205aeb6c2b40f5907fa07381d771e5dd4ff53a080167b3ea9595956bf
|