Skip to content

feat(publish): check a song for release, and write a release kit for a distributor - #48

Merged
devlato merged 1 commit into
mainfrom
feat/release-kit
Oct 20, 2026
Merged

devlato merged 1 commit into
mainfrom
feat/release-kit

Conversation

@devlato

@devlato devlato commented Oct 20, 2026

Copy link
Copy Markdown
Owner

Spotify, Apple Music and the other stores take music only from distributors, and the distributors musicians use (DistroKid, CD Baby, TuneCore, Amuse) have no API. What all of them do have is the same upload form. claudius publish SONG checks a rendered song the way stores and distributors check a release, then writes a release kit with everything that form takes. The studio and MCP get this in the next PR; uploads to SoundCloud and YouTube come after that.

claudius publish out/lofi-hollow-harbor-5 --artwork cover.jpg --artist "Ana Lima" --writer "Ana Lima" --made assisted

What it checks

  • Artwork. JPEG or PNG; square; 1400 to 10000 pixels, with 3000 × 3000 as the target; RGB, not CMYK or greyscale; 8 bits per channel; opaque; sRGB; not stored rotated; JPEG quality (read from the file's quantisation tables); sharpness; and file size.
  • Audio. Length, silence at the start and end, true peak, and a note on loudness normalization.
  • Sounds. Where each recorded sound comes from and what its licence allows. Unknown, CC BY-SA, NC and ND licences, and packs that aren't installed, block the release. CC BY packs get a credit line. Apple Loops get a note; the person's own recordings get a warning.
  • Details. The artist; how the song was made, which distributors ask about AI; titles (placeholders, promotion, featured artists, capitals, emoji); songwriters' legal names; ISRC and UPC formats; and the date.

Problems block the kit, and the command fails; warnings and notes are listed.

The kit

  • a 16-bit, 44.1 kHz WAV to upload, and a 24-bit, 48 kHz WAV;
  • the cover, exactly as given;
  • release.txt: the form's answers, what to tell a distributor about AI, the credits, the artwork rules claudius can't check, and the checks;
  • release.json: the same, for programs.

What the person gives is kept in the song's release.json. A song without one takes the artist's details from the newest release among its sibling song folders, so there's no global settings file.

The sharpness check

It measures the width of strong edges. A sharp image's sharpest 5% of edges are 1 to 3 pixels wide; in an image enlarged k times, none is narrower than about k pixels. I tuned it on macOS wallpapers and an aerial photo, against copies sips enlarged from a half, a third and a quarter of their size:

  • the three- and four-times copies are flagged, with the size they look enlarged from;
  • the two-times copies of the wallpapers are flagged, but not of the photo;
  • the originals pass, and so does a JPEG saved at quality 40, whose quality gets its own warning;
  • Apple's Tahoe wallpaper, which is soft on purpose, is flagged, so the warning tells the person to ignore it when the softness is the art.

An earlier version compared energy across frequency bands. It couldn't separate originals from enlarged copies, because JPEG's 8-pixel blocks put energy back into exactly the frequencies an enlargement empties.

How I checked it

  • cargo xtask ci passes: formatting, clippy on all three builds, the headless check, 216 tests, the doctests and the docs examples. cargo deny check and typos pass, cargo xtask site builds, and lychee --offline finds 0 broken links.
  • 14 unit tests cover the details, licences, artwork, file names and the sharpness check (on generated shapes enlarged three and four times, and a sharp original). A CLI test covers the whole flow: without artwork and an artist it fails with three problems and writes no kit; with them it writes the kit, whose 16-bit WAV decodes at 44.1 kHz and whose cover matches the input byte for byte; a later --check --json needs nothing given again.
  • Manual runs on real songs, with packs in a scratch folder:
    • a house song with a Display P3 cover gets a colour-profile warning;
    • a lo-fi song playing the CC BY Strudel piano gets its credit line in release.txt;
    • the title "HOLLOW HARBOR (Prod. by Ana)" gets a producer-tag warning;
    • a cover enlarged three times is flagged as looking enlarged from about 700 × 700.
  • publish is added to the telemetry Worker's list of commands; I'll redeploy the Worker after merge.

@devlato
devlato enabled auto-merge (squash) October 20, 2026 05:00
@devlato
devlato merged commit 5c31d96 into main Oct 20, 2026
26 checks passed
@devlato
devlato deleted the feat/release-kit branch October 20, 2026 05:10
devlato added a commit that referenced this pull request Oct 20, 2026
…API (#49)

This brings `claudius publish`
([#48](#48)) to the studio, to
assistants over MCP, and to the HTTP API.

## In the studio

**publish** in the toolbar opens a Publish dialog:

- the cover, dropped onto the studio (the native studio also takes a
path), with a preview and what it is ("3000 × 3000 JPEG");
- the details stores ask for, each sent when its field is left;
- how the song was made, as three choices that only the person can
answer;
- what stores would say of the latest render: problems in red, warnings
in yellow, notes in grey;
- **Write the release kit**, enabled once nothing stops the release.

The browser studio reads a dropped cover asynchronously and sends it the
same way. The demo keeps the details, and says that it checks nothing
and writes no files.

## In the session

- The release in the making belongs to the song: its genre, seed and
folder. Generating, remaking or opening another song starts afresh;
edits, mutations and evolution keep the details.
- Opening a song folder reads its `release.json` and its artwork.
- Saving the song, or writing the kit, puts `release.json` and
`artwork.jpg` (or `.png`) in the song's folder. That happens only once
something was given for the release, so ordinary saves write nothing
new.
- Renders check the release only once something was given for it, so
songs nobody publishes don't pay for the checks. Opening the dialog
counts as asking.

## Over MCP and HTTP

- **MCP:** a `publish` tool. It takes what the person said: the artist,
the songwriters, the artwork file, `made`, and the other details. With
`write`, it also writes the kit. What it leaves out stays as it was. Its
description, and the claudius-songs skill, tell the assistant never to
choose `made` for the person.
- **HTTP commands:** `set_release` (the details, and optionally an
artwork path, read under the session's file policy) and `write_kit`.
- **HTTP routes:**
- `POST /api/release/artwork` takes the cover's bytes. It requires an
image or octet-stream content type, so a cross-site form can't post one
without a preflight, which CORS refuses; the limit is 40 MB.
  - `GET /api/release/artwork` returns the cover as given.
- `GET /api/release/preview.png` returns it at most 512 pixels on a
side.
- **State:** `State.release` holds the details, the artwork, the checks,
the render they're of, and where the last kit went.

## How I checked it

- `cargo xtask ci` passes: formatting, clippy on all three builds, the
headless check, 217 tests, the doctests and the docs examples. `cargo
clippy -p claudius-ui --target wasm32-unknown-unknown -D warnings`,
`cargo deny check`, `typos`, `cargo xtask site` and `lychee --offline`
(0 broken links) pass too.
- A new session test runs the whole flow:
1. renders don't check a release nobody asked for, and saving doesn't
keep one;
  2. the details are checked, and the only problem left is the artwork;
  3. writing the kit is refused until there's artwork;
4. a cover sent as `text/plain` is refused, and one sent as `image/png`
is taken;
  5. the release is then ready, and the preview is 512 pixels;
6. the kit goes into `release/`, and `release.json` and `artwork.png`
into the song's folder;
7. another song starts afresh, and reopening the folder brings back the
artist and the artwork.
- The MCP test calls `publish` and gets the artwork and "how the song
was made" problems. The shared-session test refuses an artwork path from
outside the session's folders.
- Scripted studio runs (a new `publish` plan step) with screenshots:
- with a cover uploaded over HTTP, the dialog shows its preview and
size, the details, the chosen "made" answer and the checks;
- with nothing given, opening the dialog brings the checks: no artwork,
no artist, no answer about how it was made.
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