Liking cljdoc? Tell your friends :D

hive-kdenlive.addon

IAddon boundary for hive.kdenlive — exposes the MLT core, headless melt rendering, and the Kdenlive HTTP transport as MCP tools.

Pure constructor, no registration side effects: the host drives register!/initialize! after resolving addon-ctor from the manifest.

IAddon boundary for hive.kdenlive — exposes the MLT core, headless melt
rendering, and the Kdenlive HTTP transport as MCP tools.

Pure constructor, no registration side effects: the host drives
register!/initialize! after resolving `addon-ctor` from the manifest.
raw docstring

hive-kdenlive.kdenlive.client

HTTP transport to the Kdenlive scripting fork — the JVM boundary.

Strata: Pure routes/request (kdenlive.routes, portable) Port IKdenlive — call a catalog route, get data back Boundary HttpKdenlive — java.net.http, JSON wire Seam kdenlive — read at call time; rebind in tests

The wire encoding is JSON. Requests carry the route's :body as a JSON object, built by ->json here.

Responses are NOT decoded. -call answers {:ok {:status int :body "<raw JSON string>"}}, because this namespace has no JSON reader: ->json writes, and nothing reads. A caller that needs a field out of the answer (hive-creator's :finish binds [:ids 0] and [:id] to thread ids between steps) therefore cannot use this transport yet; the headless document transport answers parsed data and is what such callers run against today. This docstring previously claimed responses came back "decoded to Clojure data with keyword keys", which was never true.

Tracked as a card: give this transport a JSON reader, and with it the :timeline/insert-clip trim, which the fork cannot do in one call (its scriptInsertClip binds binId/trackId/position only) and which therefore needs a following :clip/resize to match what the headless transport does.

HTTP transport to the Kdenlive scripting fork — the JVM boundary.

Strata:
  Pure      routes/request        (kdenlive.routes, portable)
  Port      IKdenlive             — call a catalog route, get data back
  Boundary  HttpKdenlive          — java.net.http, JSON wire
  Seam      *kdenlive*            — read at call time; rebind in tests

The wire encoding is JSON. Requests carry the route's :body as a JSON
object, built by `->json` here.

Responses are NOT decoded. `-call` answers
{:ok {:status int :body "<raw JSON string>"}}, because this namespace has
no JSON reader: `->json` writes, and nothing reads. A caller that needs a
field out of the answer (hive-creator's :finish binds [:ids 0] and [:id]
to thread ids between steps) therefore cannot use this transport yet; the
headless document transport answers parsed data and is what such callers
run against today. This docstring previously claimed responses came back
"decoded to Clojure data with keyword keys", which was never true.

Tracked as a card: give this transport a JSON reader, and with it the
:timeline/insert-clip trim, which the fork cannot do in one call (its
scriptInsertClip binds binId/trackId/position only) and which therefore
needs a following :clip/resize to match what the headless transport does.
raw docstring

hive-kdenlive.kdenlive.document

The :document transport: IKdenlive over a project file, with no Kdenlive.

http-kdenlive sends a catalog route to the scripting fork. This adapter answers the same route ids against a timeline kept on disk as EDN (hive-kdenlive.mlt.timeline), and after every edit writes the MLT document melt renders beside it:

edit.hkd.edn the timeline, the source of truth edit.mlt its MLT rendering, rewritten on every change

:render/start renders that document with melt. Media is probed with melt <file> -consumer xml, the same engine the render uses, so a length the timeline trusts is a length the render will agree with.

Routes with no meaning off a running application (playback, render jobs) answer :document/unsupported-route, naming the routes that are supported.

The :document transport: IKdenlive over a project file, with no Kdenlive.

`http-kdenlive` sends a catalog route to the scripting fork. This adapter
answers the same route ids against a timeline kept on disk as EDN
(hive-kdenlive.mlt.timeline), and after every edit writes the MLT document
melt renders beside it:

  edit.hkd.edn   the timeline, the source of truth
  edit.mlt       its MLT rendering, rewritten on every change

:render/start renders that document with melt. Media is probed with
`melt <file> -consumer xml`, the same engine the render uses, so a length
the timeline trusts is a length the render will agree with.

Routes with no meaning off a running application (playback, render jobs)
answer :document/unsupported-route, naming the routes that are supported.
raw docstring

hive-kdenlive.kdenlive.json

JSON, both directions, with no dependency.

hive-kdenlive deliberately carries no JSON library, and until 2026-09-20 it showed: the encoder handled a FLAT map of scalars and rendered anything else with str, so :paths ["a.mp4" "b.mp3"] went out as the Clojure literal ["a.mp4" "b.mp3"] (no commas) and :params {:duration 5} as {:duration 5}. Both are the shapes hive-creator's :finish actually sends, to :media/import and :clip/append-effect. There was no decoder at all, so every answer came back as a raw string and a caller that needed an id out of it got nothing.

Neither had been noticed because the HTTP transport was never exercised end to end; the headless document transport was.

write and read live together so a round trip can be property-tested, which is the only way a hand-written codec stays honest.

Portable: clojure.core and clojure.string, and no host interop. There is no StringBuilder here and no (int \c), because neither exists on cljw or cljrs.

read answers Clojure data with STRING keys, exactly as the wire spells them, because the route catalog's :result names a wire field.

JSON, both directions, with no dependency.

hive-kdenlive deliberately carries no JSON library, and until 2026-09-20 it
showed: the encoder handled a FLAT map of scalars and rendered anything
else with `str`, so `:paths ["a.mp4" "b.mp3"]` went out as the Clojure
literal `["a.mp4" "b.mp3"]` (no commas) and `:params {:duration 5}` as
`{:duration 5}`. Both are the shapes hive-creator's :finish actually sends,
to :media/import and :clip/append-effect. There was no decoder at all, so
every answer came back as a raw string and a caller that needed an id out
of it got nothing.

Neither had been noticed because the HTTP transport was never exercised end
to end; the headless document transport was.

`write` and `read` live together so a round trip can be property-tested,
which is the only way a hand-written codec stays honest.

Portable: clojure.core and clojure.string, and no host interop. There is no
StringBuilder here and no `(int \c)`, because neither exists on cljw or
cljrs.

`read` answers Clojure data with STRING keys, exactly as the wire spells
them, because the route catalog's :result names a wire field.
raw docstring

hive-kdenlive.kdenlive.routes

Route catalog for the Kdenlive scripting fork's HTTP transport — pure data.

The catalog is the single source: each route names its id, HTTP method, path template, and required/optional params. The client boundary walks this data; adding an endpoint is a catalog entry, never new client code.

Portable: core + string only.

Route catalog for the Kdenlive scripting fork's HTTP transport — pure data.

The catalog is the single source: each route names its id, HTTP method,
path template, and required/optional params. The client boundary walks
this data; adding an endpoint is a catalog entry, never new client code.

Portable: core + string only.
raw docstring

hive-kdenlive.mlt.model

MLT document model: pure builders that produce xml.cljc node trees.

MLT's XML vocabulary, smallest useful subset: mlt root; LC_NUMERIC="C" so float serialization is locale-invariant profile frame geometry; the document carries one producer a clip source; resource lives in a property child playlist a track: <entry producer=.../> and <blank length=.../> refs tractor the timeline: <multitrack> of <track producer=.../> refs filter an effect on a producer/playlist/tractor transition a compositor between two tracks (a_track/b_track)

Builders return xml.cljc nodes; document assembles and xml/emit serializes. No IO, no host conditionals — core + string only.

MLT document model: pure builders that produce xml.cljc node trees.

MLT's XML vocabulary, smallest useful subset:
  mlt        root; LC_NUMERIC="C" so float serialization is locale-invariant
  profile    frame geometry; the document carries one
  producer   a clip source; resource lives in a property child
  playlist   a track: <entry producer=.../> and <blank length=.../> refs
  tractor    the timeline: <multitrack> of <track producer=.../> refs
  filter     an effect on a producer/playlist/tractor
  transition a compositor between two tracks (a_track/b_track)

Builders return xml.cljc nodes; `document` assembles and `xml/emit`
serializes. No IO, no host conditionals — core + string only.
raw docstring

hive-kdenlive.mlt.project

Read an MLT / .kdenlive document back into a summary — the inverse leg of mlt.model. Pure and portable: nodes in, data out, no IO.

A .kdenlive file is an MLT document with Kdenlive conventions: the bin lives in a playlist id="main_bin", the timeline is the tractor whose multitrack references the track playlists, and cuts are in/out attributes (clock strings or frame counts).

Read an MLT / .kdenlive document back into a summary — the inverse leg of
mlt.model. Pure and portable: nodes in, data out, no IO.

A .kdenlive file is an MLT document with Kdenlive conventions: the bin
lives in a playlist id="main_bin", the timeline is the tractor whose
multitrack references the track playlists, and cuts are in/out attributes
(clock strings or frame counts).
raw docstring

hive-kdenlive.mlt.time

Time arithmetic for MLT in/out/length attributes.

MLT accepts two time vocabularies: frame counts ("150") and clock strings ("HH:MM:SS.mmm"). Conversion between them needs the profile's fps, carried here as a [num den] fraction (a bare number means [n 1]).

Portable: core + string only, integer math where hosts disagree on rounding.

Time arithmetic for MLT in/out/length attributes.

MLT accepts two time vocabularies: frame counts ("150") and clock strings
("HH:MM:SS.mmm"). Conversion between them needs the profile's fps, carried
here as a [num den] fraction (a bare number means [n 1]).

Portable: core + string only, integer math where hosts disagree on rounding.
raw docstring

hive-kdenlive.mlt.timeline

A Kdenlive timeline as data, edited by the SAME verbs the scripting fork answers over HTTP, with no Kdenlive running.

The fork's route catalog (hive-kdenlive.kdenlive.routes) names what an agent can ask of a timeline: import media, add a track, insert a clip at a frame, open space, extract a zone. apply-verb answers those route ids against a value instead of a live application, and ->document turns the value into an MLT document melt renders. That is the :document transport: the same vocabulary, a different place for the timeline to live.

State {:name "edit" :profile {:width 1920 :height 1080 :fps 25} :next-id 1 :bin {"1" {:id "1" :resource "/clips/a.mkv" :name "a.mkv" :length 250}} :tracks [{:id "2" :name "V1" :audio? false :clips [{:id "3" :bin "1" :position 0 :in 0 :out 249}]}]}

Frames are integers throughout; :out is inclusive, as in MLT. Clips on a track never overlap: a verb that would make them overlap, or that would have to cut a clip in two, is REFUSED with a named error rather than approximated. Ids are strings, as the fork returns them.

Every verb is (fn [state params ctx] -> {:ok {:state s :result r}} | {:error ...}). ctx carries effects the pure core cannot perform: :probe, a function from a media path to {:length frames} or {:error ...}.

Portable: clojure.core and clojure.string only. No for (clojurust mishandles :when/:let in it), booleans compared with =.

A Kdenlive timeline as data, edited by the SAME verbs the scripting fork
answers over HTTP, with no Kdenlive running.

The fork's route catalog (hive-kdenlive.kdenlive.routes) names what an agent
can ask of a timeline: import media, add a track, insert a clip at a frame,
open space, extract a zone. `apply-verb` answers those route ids against a
value instead of a live application, and `->document` turns the value into
an MLT document melt renders. That is the :document transport: the same
vocabulary, a different place for the timeline to live.

State
  {:name    "edit"
   :profile {:width 1920 :height 1080 :fps 25}
   :next-id 1
   :bin     {"1" {:id "1" :resource "/clips/a.mkv" :name "a.mkv" :length 250}}
   :tracks  [{:id "2" :name "V1" :audio? false
              :clips [{:id "3" :bin "1" :position 0 :in 0 :out 249}]}]}

Frames are integers throughout; :out is inclusive, as in MLT. Clips on a
track never overlap: a verb that would make them overlap, or that would have
to cut a clip in two, is REFUSED with a named error rather than approximated.
Ids are strings, as the fork returns them.

Every verb is (fn [state params ctx] -> {:ok {:state s :result r}} | {:error ...}).
`ctx` carries effects the pure core cannot perform: :probe, a function from
a media path to {:length frames} or {:error ...}.

Portable: clojure.core and clojure.string only. No `for` (clojurust
mishandles :when/:let in it), booleans compared with `=`.
raw docstring

hive-kdenlive.mlt.transitions

Transitions between two clips, as a PLAN of verbs the route catalog already names.

Two clips on different video tracks overlap in time; the UPPER one is animated over the overlap and the audio is crossed. plan answers the calls that do it:

{:ok {:overlap [start end) :upper id :calls [[route-id params] ...]}}

Feed each call to timeline/apply-verb (the :document transport) or to the HTTP client: the same calls mean the same thing in both, and the result stays editable in the .kdenlive project as ordinary keyframes and fades.

Kinds :dissolve the upper clip's opacity crosses 0..1 :slide-left :slide-right the incoming picture pushes in from a side :slide-up :slide-down

Which clip is animated follows from the tracks, never from a flag: a later track renders on top. When the INCOMING clip is on top it fades or slides in; when the OUTGOING clip is on top it fades or slides out, uncovering the incoming one under it.

Refused by name rather than approximated: the clips do not overlap, they share a track, the incoming clip does not start inside the outgoing one, either is on an audio track, or the upper clip already carries transform keyframes a transition would overwrite.

Portable: clojure.core only. No for.

Transitions between two clips, as a PLAN of verbs the route catalog already
names.

Two clips on different video tracks overlap in time; the UPPER one is
animated over the overlap and the audio is crossed. `plan` answers the
calls that do it:

  {:ok {:overlap [start end) :upper id :calls [[route-id params] ...]}}

Feed each call to `timeline/apply-verb` (the :document transport) or to the
HTTP client: the same calls mean the same thing in both, and the result
stays editable in the .kdenlive project as ordinary keyframes and fades.

Kinds
  :dissolve                      the upper clip's opacity crosses 0..1
  :slide-left :slide-right       the incoming picture pushes in from a side
  :slide-up   :slide-down

Which clip is animated follows from the tracks, never from a flag: a later
track renders on top. When the INCOMING clip is on top it fades or slides
in; when the OUTGOING clip is on top it fades or slides out, uncovering the
incoming one under it.

Refused by name rather than approximated: the clips do not overlap, they
share a track, the incoming clip does not start inside the outgoing one,
either is on an audio track, or the upper clip already carries transform
keyframes a transition would overwrite.

Portable: clojure.core only. No `for`.
raw docstring

hive-kdenlive.mlt.xml

XML text <-> element tree, written by hand over strings.

Node shape: {:tag string :attrs [[name value] ...] :content [node-or-string ...]}. Attribute order is a vector so emission is byte-deterministic on every host.

emit : node -> string (declaration, one-space indent, trailing newline). parse: string -> {:ok node} | {:error :mlt/xml-malformed :at int :message string}.

Portable: clojure.core and clojure.string only, no reader conditionals.

XML text <-> element tree, written by hand over strings.

Node shape: {:tag string :attrs [[name value] ...] :content [node-or-string ...]}.
Attribute order is a vector so emission is byte-deterministic on every host.

`emit` : node -> string (declaration, one-space indent, trailing newline).
`parse`: string -> {:ok node} | {:error :mlt/xml-malformed :at int :message string}.

Portable: clojure.core and clojure.string only, no reader conditionals.
raw docstring

hive-kdenlive.render

Headless melt rendering — the JVM-only boundary of the MLT core.

Strata: Pure melt-argv — command vector from data Port IRender — who can render a document Boundary MeltRenderer — spawns the melt process Seam renderer — read at call time; rebind in tests

Results are data: {:ok {...}} | {:error kw ...}, same shape as mlt.xml.

Headless melt rendering — the JVM-only boundary of the MLT core.

Strata:
  Pure      melt-argv        — command vector from data
  Port      IRender          — who can render a document
  Boundary  MeltRenderer     — spawns the melt process
  Seam      *renderer*       — read at call time; rebind in tests

Results are data: {:ok {...}} | {:error kw ...}, same shape as mlt.xml.
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close