Skip to content

feat(browser): add /live/browser/* handlers for loading presets via OSC#200

Open
keshav55 wants to merge 1 commit intoideoforms:masterfrom
keshav55:feat/browser-load-handler
Open

feat(browser): add /live/browser/* handlers for loading presets via OSC#200
keshav55 wants to merge 1 commit intoideoforms:masterfrom
keshav55:feat/browser-load-handler

Conversation

@keshav55
Copy link
Copy Markdown

What

Adds a new BrowserHandler that exposes Live's browser API over OSC, so external clients can load instruments and audio effects onto tracks without asking humans to drag presets.

Four new endpoints:

  • /live/browser/list_categories — enumerate top-level browser roots
  • /live/browser/list_children [category, max_items=30] — names of one level under a root
  • /live/browser/load_by_query [track_index, query] — search across user-facing roots, load first loadable match onto track
  • /live/browser/load_from_category [track_index, category, query] — restricted-scope version

Search is capped (MAX_ITEMS_SCANNED=2000, MAX_DEPTH=6) so pathological queries don't walk the full library.

Why

AbletonOSC already covers clips, tracks, devices, scenes, song. The missing piece for end-to-end programmatic session building (agents, automated test sessions, education tools) was preset loading. Currently external clients have to ask humans to drag instruments from the browser, which is a UX hole for agent-driven workflows.

With these endpoints, a client can now go from empty Live project → tracks with instruments loaded → MIDI clips written → playing — all via OSC.

Extra: reload_imports() ordering fix in manager.py

On /live/api/reload after adding a new submodule (first reload after browser.py exists), the previous implementation would hit importlib.reload(abletonosc.browser) before abletonosc.browser was an attribute of the cached package, throw AttributeError, and short-circuit the rest of the reload. The fix: reload the package first (re-runs __init__.py, picks up new from .browser import BrowserHandler), then iterate submodules tolerantly (getattr(abletonosc, modname, None); skip if absent).

Test notes

  • Tested on Ableton Live 12.3.7 on macOS.
  • Manual smoke: /live/browser/list_categories returns all 12 expected roots; /live/browser/load_from_category [track, 'audio_effects', 'compressor'] correctly inserts a Compressor onto the target track.
  • Reload path tested both cold (fresh Live boot) and hot (/live/api/reload after adding browser.py in-place).

Backwards compatibility

Purely additive — no existing endpoints or reply shapes changed.

Context

This was built while driving Ableton from an AI agent (atris-studio, private). Happy to iterate on naming/shape if you'd prefer a different convention for the browser endpoints.

Problem
-------
AbletonOSC exposes clip/track/device/scene/song handlers but no way to
LOAD instruments or audio effects onto tracks programmatically. External
clients wanting to drive Live end-to-end have to ask humans to drag
presets from the browser, which is a UX hole for agent-driven setups.

Solution
--------
New BrowserHandler in abletonosc/browser.py that uses Live's
Live.Application.get_application().browser to search the stock browser
tree and load the first matching item onto a specified track.

New OSC endpoints:
  /live/browser/list_categories
      → replies (category_names...) for the top-level roots
      (sounds, instruments, drums, audio_effects, midi_effects, samples,
       plugins, clips, user_library, current_project, packs, max_for_live)

  /live/browser/list_children [category, max_items=30]
      → replies (category, child_names...) one level under the root —
      useful for discovery before loading.

  /live/browser/load_by_query [track_index, query]
      → BFS the user-facing roots in priority order (sounds → instruments
      → drums → samples → packs → user_library). Select track_index,
      load_item() the first loadable item whose name contains `query`
      (case-insensitive substring). Reply with the loaded item name or ""
      if no match.

  /live/browser/load_from_category [track_index, category, query]
      → same but restricted to one top-level root. Safer for avoiding
      accidental matches across categories.

Search is capped at MAX_ITEMS_SCANNED=2000 and MAX_DEPTH=6 to avoid
walking 100k-item preset libraries on pathological queries.

Reload-ordering fix
-------------------
`manager.py`'s `reload_imports()` re-ordered so the PACKAGE is reloaded
BEFORE individual submodules. Previously, `importlib.reload(abletonosc.browser)`
would fail silently when `browser` wasn't yet an attribute of the cached
`abletonosc` package (as happens on the first reload after adding a new
submodule), short-circuiting the whole reload. Now we reload the package
first (which re-runs __init__.py and picks up any new `from .browser import`
lines), then iterate known submodules tolerantly.

Why it matters
--------------
For external tooling that programmatically builds Live sessions (agent-driven
production, automated test sessions, education tools), this closes the one
remaining "you still need a human to drag presets" gap. Combined with
existing track/clip/device handlers, it's now possible to drive a session
from empty project → loaded instruments → MIDI clips → playing — all via
OSC, zero clicks.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant