Skip to main content

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())

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",
    clip_id=1
)

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

kdenlive_api-0.1.0.tar.gz (41.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

kdenlive_api-0.1.0-py3-none-any.whl (23.2 kB view details)

Uploaded Python 3

File details

Details for the file kdenlive_api-0.1.0.tar.gz.

File metadata

  • Download URL: kdenlive_api-0.1.0.tar.gz
  • Upload date:
  • Size: 41.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.6.11

File hashes

Hashes for kdenlive_api-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9bc2cbe34e36fcf0673d1ac15af13d4b32e14bb2a61968592598016c61d529f4
MD5 5d49f485bf73c633ecc995af3b79fa16
BLAKE2b-256 cbad2dc9e908f37d9f5fc9798e7f57578f94994e2af3ec9cc92c345651a60a89

See more details on using hashes here.

File details

Details for the file kdenlive_api-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for kdenlive_api-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5d30b71a22f2eb056e6d6ab6b36dc953b7a70b91aaa3fa48368c73e6d9f649b7
MD5 9871439bfa4506d811dd59a8362eaba2
BLAKE2b-256 64303eddc24bd2d4dab2e76c7aae074d632edf8cbccc0c45305e3a043b3c1bb3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page