kestra-io / kestra-io/plugin-sonilo

Add Sonilo Plugin

Open
#2 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area/plugin
Dominant language
Java
Stars
0
Forks
0
Avg merge
12h 56m
Merged PRs (30d)
3

Description

Summary

Sonilo (https://platform.sonilo.com) is an AI video-to-music and sound-design API: given a video, it generates original, production-ready music or sound effects synced to the video's scenes, cuts, and emotional arc, with no manual editing. A Kestra plugin lets flows call Sonilo directly after a video is produced or ingested — e.g. score a completed video with a matched soundtrack as part of an automated delivery pipeline, with no custom scripting task in between.

Motivation

Teams currently automating video delivery pipelines have no first-class way to attach a matched soundtrack or sound design step to a flow — they either script raw HTTP calls or handle audio matching manually outside Kestra. A Sonilo plugin lets a flow react to a completed-video event (upload, render-finished webhook, scheduled batch) and produce a scored asset in the same execution, before handoff to downstream delivery/publishing tasks. This fits alongside Kestra's existing media/AI-generation style plugins (submit job, poll or stream for the result).

Context

No parent issue or prior discussion found — this is a new integration proposal. Sonilo's async job shape (submit → stream NDJSON or poll a task id) matches the "submit + poll for completion" pattern used by other Kestra plugins that wrap async AI/media-generation APIs; use one of those as a structural reference during implementation.

API Reference

  • Official docs: https://platform.sonilo.com/docs (also available in French at https://platform.sonilo.com/fr/docs)
  • Authentication: Bearer API key — Authorization: Bearer sk_<api_key>, generated from the Sonilo dashboard
  • Base URL pattern: https://api.sonilo.com/v1/
  • SDK / client library: none official found — use Kestra's internal HTTP client

Note for the developer: endpoint paths below were extracted from the docs site via automated page summarization, not read verbatim. Confirm exact paths, request/response shapes, and whether a webhook/callback mechanism exists (none was found during research) against https://platform.sonilo.com/docs before implementation.

  • POST /v1/video-to-music — generate a soundtrack matched to an input video (sync stream or async mode returning a task id)
  • POST /v1/text-to-music — generate music from a text prompt
  • POST /v1/video-to-sfx — generate sound effects matched to an input video (async, returns a task id)
  • POST /v1/text-to-sfx — generate sound effects from a text prompt (async, returns a task id)
  • POST /v1/audio-ducking — mix a voice track with background music/sfx
  • GET /v1/tasks/{task_id} — poll the status/result of an async generation task

Gradle Dependencies

No additional Gradle dependencies required — Sonilo has no official Java SDK, and its API is plain REST/JSON, so implement it with Kestra's internal HTTP client (io.kestra.core.http.client) and core Jackson serializers.

Kestra framework inclusions (do not add as dependencies):
Kestra's internal HTTP client and Jackson serializers are provided by the framework. Only list an explicit HTTP dependency when an official SDK is required — never list OkHttp, Apache HttpComponents, or java.net.http wrappers.

Plugin Structure

  • Repository: plugin-sonilo
  • Namespace: io.kestra.plugin.sonilo
  • Sub-plugins: none — flat package, consistent with the existing scaffold
  • Categories: BUSINESS

Task class naming: task class names must not repeat the plugin or package name as a
prefix or suffix. Use concise action names: GenerateMusicFromVideo, GenerateMusicFromText,
GenerateSfxFromVideo, GenerateSfxFromText, DuckAudio, Trigger — not SoniloGenerateMusic,
GenerateMusicSonilo, etc.

Suggested Tasks

  1. Implement an AbstractSonilo base class holding the apiToken (secret) and baseUrl connection properties, shared by every task and the trigger
  2. GenerateMusicFromVideoPOST /v1/video-to-music, input video via Property<Data> or internal-storage URI, output the generated audio to internal storage
  3. GenerateMusicFromTextPOST /v1/text-to-music
  4. GenerateSfxFromVideoPOST /v1/video-to-sfx
  5. GenerateSfxFromTextPOST /v1/text-to-sfx
  6. DuckAudioPOST /v1/audio-ducking, mixing a voice track with a music/sfx track
  7. Add a polling Trigger that watches a submitted async task id (GET /v1/tasks/{task_id}) and fires an Execution once it reaches a terminal (completed/failed) state
  8. Write unit + integration tests with WireMock (no official Sonilo Testcontainers image exists)
  9. Add package-info.java with @PluginSubGroup(category = PluginSubGroup.PluginCategory.BUSINESS)
  10. Add metadata/index.yaml and plugin icon SVG
  11. Add YAML examples and the package-level how-to doc (doc/io.kestra.plugin.sonilo.md)

YAML Examples

Example 1 — Generate a soundtrack for a rendered video
id: score_completed_video
namespace: company.media

inputs:
  - id: video_uri
    type: STRING

tasks:
  - id: generate_music
    type: io.kestra.plugin.sonilo.GenerateMusicFromVideo
    apiToken: "{{ secret('SONILO_API_TOKEN') }}"
    video: "{{ inputs.video_uri }}"
Example 2 — Generate sound effects from a text prompt and log the result
id: generate_sfx
namespace: company.media

tasks:
  - id: generate_sfx
    type: io.kestra.plugin.sonilo.GenerateSfxFromText
    apiToken: "{{ secret('SONILO_API_TOKEN') }}"
    prompt: "Footsteps on gravel, approaching camera"

  - id: log_result
    type: io.kestra.plugin.core.log.Log
    message: "Generated SFX available at {{ outputs.generate_sfx.audioUri }}"
Example 3 — React when an async Sonilo generation task completes
id: on_sonilo_task_complete
namespace: company.media

triggers:
  - id: wait_for_task
    type: io.kestra.plugin.sonilo.Trigger
    apiToken: "{{ secret('SONILO_API_TOKEN') }}"
    taskId: "{{ trigger.taskId }}"
    interval: PT30S

tasks:
  - id: handle_result
    type: io.kestra.plugin.core.log.Log
    message: "Sonilo task {{ trigger.taskId }} finished: {{ trigger.status }}"

Acceptance Criteria

Functional
  • Authentication task / abstract base class implemented
  • Core generation tasks for each resource group (music, sfx, ducking)
  • At least one polling trigger
  • Unit + integration tests pass (./gradlew test)
  • Build passes with ./gradlew build
Kestra Plugin Coding Standards
  • HTTP calls use Kestra's internal HTTP client (io.kestra.core.http.client) — no OkHttp, Apache HttpClient, or similar
  • All new properties use Property<T> — no legacy @PluginProperty(dynamic = true) on new code
  • Secret/credential properties (apiToken) annotated with @PluginProperty(secret = true) and @ToString.Exclude
  • Every property and output has a @Schema annotation
  • Task classes carry the five mandatory Lombok annotations (@SuperBuilder, @ToString, @EqualsAndHashCode, @Getter, @NoArgsConstructor)
  • Logging via runContext.logger() only
  • JSON serialization uses Jackson mappers from io.kestra.core.serializers
  • All Property<T> fields support Kestra expression language (template rendering)
Documentation & Structure
  • @Plugin(examples = ...) entries each set full = true with a complete runnable flow (id + namespace + tasks/triggers)
  • Sensitive values in examples use {{ secret('SECRET_NAME') }}
  • package-info.java with @PluginSubGroup(category = PluginSubGroup.PluginCategory.BUSINESS)
  • metadata/index.yaml and plugin icon SVG present

Repository Setup Checklist

1. Add to Sanity check page

Add this plugin to the Sanity check Notion page.

2. Run scoped Terraform apply

Run the following from infra/terraform/github:

terraform apply \
    -target='github_repository.repo["plugin-sonilo"]' \
    -target='github_issue_labels.plugins["plugin-sonilo"]' \
    -target='github_repository_ruleset.branch["plugin-sonilo"]'

Notes for the developer

  • No webhook/callback mechanism was found in the publicly available docs — the trigger must poll GET /v1/tasks/{task_id} rather than react to a push notification. Confirm this against the docs; if a webhook exists, prefer a RealtimeTrigger instead.
  • Sonilo is self-serve, pay-as-you-go (Stripe-billed credits, no free tier, no sales gate for basic access) — this is why it's proposed as an OSS plugin (plugin-sonilo, already the current repository) rather than EE.
  • Exact endpoint paths, request/response payloads, and error formats should be re-verified directly against https://platform.sonilo.com/docs before implementation — the research pass here used automated page summarization, not a verbatim read.

View as Artifact

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

First verify Sonilo's endpoint paths, request/response shapes, and callback support in the linked API documentation, then inspect existing Kestra plugins that use submit-and-poll flows. Implement the listed tasks and polling Trigger with WireMock unit and integration coverage, and run ./gradlew test and ./gradlew build. Done also requires package-info.java, metadata/index.yaml, the icon SVG, YAML examples, and doc/io.kestra.plugin.sonilo.md.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
api, backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.