Skip to main content

Non-linear video editor component for Streamlit with multi-track timeline

Project description

streamlit-nle video editor screenshot

streamlit-nle

A professional non-linear video editor component for Streamlit

PyPI version Python versions License Downloads


streamlit-nle (Non-Linear Editor) is a fully-featured video/audio timeline editor that runs inside any Streamlit application. It brings real NLE capabilities — multi-track timelines, clip manipulation, transport controls, media management, and keyboard shortcuts — directly into the browser, with no server-side dependencies beyond Streamlit itself.

Features

Multi-Track Timeline

  • Unlimited video & audio tracks — add as many tracks as your project needs
  • Drag-and-drop clip placement — move clips freely along the timeline or between tracks
  • Clip trimming — drag the left/right edge of any clip to trim its in/out points
  • Razor tool — split a clip at the playhead position with a single click
  • Snap-to-grid — clips snap to second boundaries for clean edits (toggle with S)
  • Track controls — per-track Mute, Solo, and Lock buttons

Video Preview

  • Integrated preview panel — displays the video file at the current playhead position
  • Transport-linked playback — preview follows the playhead in real time during play
  • Frame-accurate seeking — click the timeline ruler or use keyboard shortcuts to scrub
  • Timecode overlay — always-visible timecode in HH:MM:SS format over the preview

Audio Engine

  • Real-time audio playback — HTML5 Audio elements are created per clip, synced to the timeline clock
  • Drift correction — audio position is automatically corrected when it drifts more than 150 ms from the playhead
  • Mute / Solo aware — the engine respects per-track mute and solo states
  • Automatic cleanup — audio elements are properly disposed on unmount to prevent memory leaks

Media Bin

  • File import — click Import or the Media Bin's + Import link to add video, audio, or image files
  • Drag from bin to timeline — drag any media item from the bin and drop it onto a track (or outside existing tracks to auto-create a new one)
  • Thumbnail generation — video files are sampled at 2 seconds for an automatic thumbnail (with a 5-second timeout fallback)
  • Metadata display — each item shows its type (VIDEO / AUDIO / IMAGE), duration, and file size
  • Extension-based MIME detection — supports .mp4, .webm, .mov, .avi, .mkv, .mp3, .wav, .ogg, .m4a, .flac, .aac, .jpg, .png, .gif, .webp, and more

Resizable & Collapsible Layout

  • Three independent sections — Preview, Media Bin, and Timeline
  • Drag-to-resize dividers — grab the divider between sections and drag to redistribute space
  • Collapse any section — click the section header's arrow to collapse it to a slim tab; click again to restore
  • Remembers proportions — section sizes persist during the session

Editing Tools

  • Select tool (V) — click to select clips, drag to move them
  • Razor tool (C) — click a clip to split it at the playhead
  • Mark In / Out (I / O) — set in/out markers on the timeline ruler
  • Loop region (L) — toggle looping between the in and out markers
  • Undo / Redo (⌘Z / ⌘⇧Z) — full history stack with unlimited undo
  • Copy / Paste (⌘C / ⌘V) — duplicate clips across tracks
  • Delete (Delete / Backspace) — remove selected clips

Keyboard Shortcuts

Key Action
Space or K Play / Pause
J Jump back 5 seconds
/ Nudge playhead by 1 second
Home Jump to start (0:00)
End Jump to end of last clip
V Select tool
C Razor tool
S Toggle snap
I Set mark in
O Set mark out
L Toggle loop
⌘Z Undo
⌘⇧Z Redo
⌘C Copy selected clip
⌘V Paste clip
Delete Remove selected clip

Installation

pip install streamlit-nle

Note: The PyPI package is named streamlit-nle because streamlit-video-editor was already registered. The Python import remains streamlit_video_editor.

Quick Start

import streamlit as st
from streamlit_video_editor import st_video_editor

st.set_page_config(layout="wide")
st.title("🎬 Video Editor")

result = st_video_editor(key="editor")

if result:
    st.write(f"Tracks: {len(result.get('tracks', []))}")
    st.write(f"Playhead: {result.get('playhead', 0):.1f}s")

API Reference

st_video_editor

st_video_editor(
    tracks: list[dict] | None = None,
    height: int = 700,
    key: str | None = None,
) -> dict | None

Parameters

Parameter Type Default Description
tracks list[dict] or None None Initial track data. When None, the editor starts empty and tracks can be created interactively.
height int 700 Height of the editor component in pixels. The layout adapts to the available space.
key str or None None An optional key that uniquely identifies this component. Required when placing multiple editors on one page.

Return Value

Returns a dict (or None before first interaction) with the following structure:

{
    "tracks": [
        {
            "id": "track-1",
            "name": "Video 1",
            "type": "video",          # "video" or "audio"
            "muted": False,
            "solo": False,
            "locked": False,
            "clips": [
                {
                    "id": "clip-1",
                    "name": "Interview_A.mp4",
                    "start": 2.0,      # start position in seconds on timeline
                    "duration": 12.0,  # clip length in seconds
                    "src": "blob:...", # object URL of media file (browser-local)
                    "type": "video",
                    "offset": 0.0,     # source-media offset (for trimmed clips)
                    "mediaId": "m-1"   # reference to media bin item
                }
            ]
        }
    ],
    "playhead": 7.25  # current playhead position in seconds
}

Data Structures

Track

Field Type Description
id str Unique track identifier (auto-generated if omitted)
name str Display name shown in the track header
type str "video" or "audio" — determines track color scheme
muted bool Whether the track's audio is muted
solo bool Whether the track is soloed (only soloed tracks play audio)
locked bool Whether clips on this track are locked from editing
clips list[dict] List of clip objects on this track

Clip

Field Type Description
id str Unique clip identifier
name str Display name (usually the file name)
start float Start time on the timeline in seconds
duration float Duration in seconds
src str Object URL or data URL for the media file
type str "video", "audio", or "image"
offset float Offset into the source media (for trimmed clips)
mediaId str Reference to the corresponding media bin item

Usage Examples

Pre-populated Timeline

import streamlit as st
from streamlit_video_editor import st_video_editor

initial_tracks = [
    {
        "id": "v1",
        "name": "Main Video",
        "type": "video",
        "muted": False,
        "solo": False,
        "locked": False,
        "clips": [
            {"id": "c1", "name": "Intro", "start": 0, "duration": 5,
             "type": "video", "offset": 0},
            {"id": "c2", "name": "Interview", "start": 5, "duration": 15,
             "type": "video", "offset": 0},
        ],
    },
    {
        "id": "a1",
        "name": "Background Music",
        "type": "audio",
        "muted": False,
        "solo": False,
        "locked": False,
        "clips": [
            {"id": "c3", "name": "Ambient.mp3", "start": 0, "duration": 20,
             "type": "audio", "offset": 0},
        ],
    },
]

result = st_video_editor(tracks=initial_tracks, height=800, key="editor")

Reading the Edit Back

result = st_video_editor(key="editor")

if result and result.get("tracks"):
    for track in result["tracks"]:
        st.subheader(f"🎚️ {track['name']} ({track['type']})")
        for clip in track.get("clips", []):
            col1, col2, col3 = st.columns(3)
            col1.metric("Clip", clip["name"])
            col2.metric("Start", f"{clip['start']:.1f}s")
            col3.metric("Duration", f"{clip['duration']:.1f}s")

Controlling Editor Height

# Compact mode for dashboards
result = st_video_editor(height=400, key="compact")

# Full-screen editing
result = st_video_editor(height=900, key="fullscreen")

Architecture

The component is built with a React 18 frontend communicating with Streamlit via the bidirectional component API (streamlit-component-lib).

┌─────────────────────────────────────────────┐
│  Python (Streamlit)                         │
│  st_video_editor(tracks, height, key)       │
│       ↓ args         ↑ componentValue       │
├─────────────────────────────────────────────┤
│  React Frontend (iframe)                    │
│  ┌──────────────────────────────────────┐   │
│  │ Toolbar (transport, tools, zoom)     │   │
│  ├────────────────────┬─────────────────┤   │
│  │ Video Preview      │ Media Bin       │   │
│  │ (HTML5 <video>)    │ (imported files)│   │
│  ├────────────────────┴─────────────────┤   │
│  │ Timeline                             │   │
│  │ ┌─────────┬──────────────────────┐   │   │
│  │ │ Track   │ Clips on grid        │   │   │
│  │ │ headers │ (drag, trim, razor)  │   │   │
│  │ └─────────┴──────────────────────┘   │   │
│  └──────────────────────────────────────┘   │
└─────────────────────────────────────────────┘
  • Transport systemrequestAnimationFrame loop drives the playhead at 30 fps during playback
  • Audio engine — one HTMLAudioElement per clip, with seek + drift correction on every animation frame
  • Video preview — a single <video> element that is currentTime-synced to the playhead
  • State — React useState with a custom undo/redo history stack; changes are debounced before being sent to Streamlit via Streamlit.setComponentValue()
  • Layout — CSS flexbox with draggable resize dividers; section heights are stored in component state

Browser Compatibility

Browser Status
Chrome / Edge 90+ ✅ Full support
Firefox 90+ ✅ Full support
Safari 15+ ✅ Full support
Mobile browsers ⚠️ Usable but optimized for desktop

Requirements

  • Python 3.8+
  • Streamlit ≥ 1.28.0

License

MIT — see LICENSE for details.

Links

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

streamlit_nle-0.1.1.tar.gz (434.4 kB view details)

Uploaded Source

Built Distribution

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

streamlit_nle-0.1.1-py3-none-any.whl (438.6 kB view details)

Uploaded Python 3

File details

Details for the file streamlit_nle-0.1.1.tar.gz.

File metadata

  • Download URL: streamlit_nle-0.1.1.tar.gz
  • Upload date:
  • Size: 434.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for streamlit_nle-0.1.1.tar.gz
Algorithm Hash digest
SHA256 a97a16b420acc4c62e7b8c430a0d9f3f47fce956445c68b0301ad3046bd75981
MD5 97531fcafd04074dda3bdac02f045a02
BLAKE2b-256 40fa01b1bfa925f856a6acb553d1f00b6c39961e014cc71f321d41bda1161f32

See more details on using hashes here.

File details

Details for the file streamlit_nle-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: streamlit_nle-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 438.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for streamlit_nle-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 bea50d26d9f5d8732f3f2506f07738e63ba09439e7d47e31a63bf0b4493f6ac7
MD5 384edf07e46a14c4cd23f24793e802f1
BLAKE2b-256 27012959093d25f97bae230b51183ba34639777fb97f719125cdea40b0f2d8cc

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