Add pytest suite, unlimited daily cap, and vocal batch generator.
Tests cover curation, Lyria, queue, and API routes. Setting max_new_songs_per_day to 0 disables the limit; generate-batch runs 20 curated multilingual vocal directions. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from ozan_radio.dj import TrackPlan
|
||||
from ozan_radio.settings import LyriaSettings
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_plan() -> TrackPlan:
|
||||
return TrackPlan(
|
||||
id="abc12345",
|
||||
title="Test Transmission",
|
||||
mood="hypnotic test mood",
|
||||
dj_line="Testing on air.",
|
||||
lyria_prompt="Instrumental. 85 BPM desert dub with saz and ney. No vocals.",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def lyria_mix() -> LyriaSettings:
|
||||
return LyriaSettings(
|
||||
model="lyria-3-pro-preview",
|
||||
vocal_mode="mix",
|
||||
language="auto",
|
||||
singer_profile="",
|
||||
output_format="mp3",
|
||||
prefer_instrumental=False,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def lyria_instrumental() -> LyriaSettings:
|
||||
return LyriaSettings(
|
||||
model="lyria-3-pro-preview",
|
||||
vocal_mode="instrumental",
|
||||
language="auto",
|
||||
singer_profile="",
|
||||
output_format="mp3",
|
||||
prefer_instrumental=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def songs_dir(tmp_path: Path) -> Path:
|
||||
"""Minimal song library with one keeper and one skip-rated track."""
|
||||
d = tmp_path / "songs"
|
||||
d.mkdir()
|
||||
keeper = {
|
||||
"id": "11111111",
|
||||
"title": "Sahara Test",
|
||||
"mood": "warm",
|
||||
"dj_line": "Test line.",
|
||||
"lyria_prompt": "saz and ney",
|
||||
"lyrics": "",
|
||||
"file": "11111111_Sahara_Test.mp3",
|
||||
"saved_at": "2026-06-07T12:00:00+00:00",
|
||||
"curation": {
|
||||
"rating": "love",
|
||||
"shuffle_weight": 1.5,
|
||||
"public_playlist": True,
|
||||
"listener": "test",
|
||||
"notes": "Gold template.",
|
||||
"loved": ["saz", "ney"],
|
||||
"disliked": ["fuzz guitar"],
|
||||
"avoid_in_successors": ["electric guitar"],
|
||||
"clone_prompt_hints": "saz first",
|
||||
},
|
||||
}
|
||||
skipped = {
|
||||
"id": "22222222",
|
||||
"title": "Bad Intro",
|
||||
"mood": "",
|
||||
"dj_line": "",
|
||||
"lyria_prompt": "",
|
||||
"lyrics": "",
|
||||
"file": "22222222_Bad_Intro.mp3",
|
||||
"saved_at": "2026-06-06T12:00:00+00:00",
|
||||
"curation": {
|
||||
"rating": "skip",
|
||||
"public_playlist": False,
|
||||
"listener": "test",
|
||||
"notes": "Removed.",
|
||||
"loved": [],
|
||||
"disliked": ["thin intro"],
|
||||
"avoid_in_successors": [],
|
||||
"clone_prompt_hints": "",
|
||||
},
|
||||
}
|
||||
(d / "11111111_Sahara_Test.mp3").write_bytes(b"ID3fake")
|
||||
(d / "22222222_Bad_Intro.mp3").write_bytes(b"ID3fake")
|
||||
(d / "11111111.meta.json").write_text(json.dumps(keeper), encoding="utf-8")
|
||||
(d / "22222222.meta.json").write_text(json.dumps(skipped), encoding="utf-8")
|
||||
return d
|
||||
Reference in New Issue
Block a user