Skip to content

Add a music profile to audio assets, read from ID3/Vorbis/MP4 tags - #46

Merged
davior merged 1 commit into
mainfrom
claude/admiring-planck-ggdijx
Oct 26, 2026
Merged

davior merged 1 commit into
mainfrom
claude/admiring-planck-ggdijx

Conversation

@davior

@davior davior commented Oct 26, 2026

Copy link
Copy Markdown
Owner

Step 1 of making GAM a usable soundtrack source for Gecko Video Creator. Before this, an audio asset carried nothing about what it sounds like, so GVC could only choose music by its title.

What's added

New nullable columns on Asset:

  • music_genres, music_moods, music_instruments: JSON lists
  • music_bpm
  • music_key
  • music_vocals: a nullable bool, where NULL means "not known", not "instrumental"
  • music_energy: low / medium / high

Parsing lives in backend/app/music.py.

Filled from the file's own tags, on upload and by the existing "re-read file metadata" job (harvest_attribution). Audio only:

  • ID3: TCON, TMOO, TBPM, TKEY, and USLT lyrics, which means the track has vocals
  • Vorbis: GENRE, MOOD, BPM, INITIALKEY, LYRICS
  • MP4: genre, tmpo
  • A genre of "Instrumental" means no vocals.

These come from the ffprobe tag block that probe.py already returns. Like harvested attribution, they only fill blanks and are stamped embedded.

Other ways in:

  • URL import: a site's genre (SoundCloud, Bandcamp) is carried onto audio imports.
  • By hand: a new Music tab on audio assets, and PATCH /api/assets/{id}. A list replaces the stored one, and [] clears it.

Search:

  • Genres, moods, instruments, "instrumental"/"vocals" and " energy" go into a new asset_fts.music_text column, and into the asset's embedding text.
  • BPM and key stay out of the text, because bare numbers collide with titles.
  • Adding the column means rebuilding the FTS table. Migration d71e4b9a2c86 does the rebuild and repopulates the index, the same way 9c2e08b4a1f7 did for attribution. Adding the columns is a separate revision, c3a8f15e7b20.

Renamed: apply_embedded_attribution → apply_embedded_fields, since it now fills music fields as well.

Design notes are in docs/music-profile.md.

Not in this PR

  • GVC's search_assets returning the profile to the assistant (step 2, in gvc).
  • Analysing the audio itself (BPM, energy, vocals, CLAP embeddings).
  • BPM range filters on /api/assets.

Testing

  • pytest -q: 1316 passed.
    • New tests/test_music.py, which runs against real MP3 and FLAC fixtures built by make_fixtures.py.
    • A migration test in test_migrations.py that seeds an index entry and a profile, upgrades, checks both are searchable, then downgrades.
  • npm run format:check, npm run lint, npm test (412 passed, including the new MusicPanel.test.tsx) and npm run build are all clean.

Generated by Claude Code

So Gecko Video Creator can find a soundtrack that fits a scene instead of
choosing by title. New nullable columns on Asset: genres, moods,
instruments (JSON lists), bpm, key, vocals and energy.

- Harvested on upload and by the existing re-read job from the tag block
  ffprobe already returns: TCON/TMOO/TBPM/TKEY and lyrics for ID3, the
  Vorbis GENRE/MOOD/BPM/INITIALKEY/LYRICS comments, MP4 genre/tmpo.
  Fills blanks only, stamped "embedded". Audio only.
- URL import carries a site's genre onto audio imports.
- Editable in a new Music tab and through PATCH /api/assets/{id}.
- Indexed in a new asset_fts music_text column (rebuild + repopulate
  migration) and in the asset's embedding text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCL4jm6ogZBBhnjEfBBaXU

davior commented Oct 26, 2026

Copy link
Copy Markdown
Owner Author

Backend (pytest) was cancelled before any test ran. It stopped in the "Ensure ffmpeg is available" step: the Azure Ubuntu mirror was serving ffmpeg's packages at about 30 s each, and the run was cancelled after 10 minutes while still downloading libavcodec60 (job log). The failure is in apt, not in this diff. The full backend suite (1316 tests) passes locally on Python 3.13, and Frontend and Docker are green on this commit.

I can't re-run the job from here (the integration gets a 403). Please re-run the failed job: Re-run jobs → Re-run failed jobs on the run.


Generated by Claude Code

@davior
davior marked this pull request as ready for review October 26, 2026 10:28
@davior
davior merged commit 2f15789 into main Oct 26, 2026
5 of 6 checks passed
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.

2 participants