screenshot fixture: add songs with dropped lines, missing span, low-confidence notes

This commit is contained in:
2026-07-28 00:48:08 -07:00
parent 6a98ea0f21
commit 8e89b08571
+66 -4
View File
@@ -6,6 +6,7 @@ with a fixed set of jobs covering every status, then screenshots it.
One-time setup: pip install -e '.[dev]' && playwright install chromium One-time setup: pip install -e '.[dev]' && playwright install chromium
Run: python scripts/screenshot_webui.py Run: python scripts/screenshot_webui.py
""" """
import json
import tempfile import tempfile
import threading import threading
import time import time
@@ -16,12 +17,36 @@ import uvicorn
from playwright.sync_api import sync_playwright from playwright.sync_api import sync_playwright
from songtrace.config import Config from songtrace.config import Config
from songtrace.pipeline import PipelineError, PipelineResult from songtrace.pipeline import PipelineError, PipelineResult, song_slug
from songtrace.web import create_app from songtrace.web import create_app
REPO_ROOT = Path(__file__).resolve().parent.parent REPO_ROOT = Path(__file__).resolve().parent.parent
OUT_IMAGE = REPO_ROOT / "docs" / "images" / "webui.png" OUT_IMAGE = REPO_ROOT / "docs" / "images" / "webui.png"
STAGES = ["acquire", "separate", "transcribe", "align", "pitch", "notes", "grid", "package"] STAGES = ["acquire", "separate", "transcribe", "align", "pitch", "notes", "grid", "package"]
DEFAULT_LYRICS = "[verse]\n...\n[chorus]\n...\n"
# public domain (16th century English folk song, author unknown)
GREENSLEEVES_LYRICS = """\
Alas, my love, you do me wrong
To cast me off discourteously
For I have loved you well and long
Delighting in your company
Greensleeves was all my joy
Greensleeves was my delight
Greensleeves was my heart of gold
And who but my lady Greensleeves
I have been ready at your hand
To grant whatever you would crave
I have both waged life and land
Your love and good will for to have
Greensleeves was all my joy
Greensleeves was my delight
Greensleeves was my heart of gold
And who but my lady Greensleeves
"""
# order matters: the app has one worker thread, so each job runs in submission # order matters: the app has one worker thread, so each job runs in submission
# order. "running" must be submitted after the done/failed ones complete and # order. "running" must be submitted after the done/failed ones complete and
@@ -33,6 +58,27 @@ SONGS = [
("Pink Moon", "Nick Drake", "done", ("Pink Moon", "Nick Drake", "done",
{"warnings": ["2 low-confidence notes near 0:38–0:41", {"warnings": ["2 low-confidence notes near 0:38–0:41",
"1 low-confidence note near 1:22–1:24"]}), "1 low-confidence note near 1:22–1:24"]}),
("Landslide", "Fleetwood Mac", "done",
{"warnings": ["1 low-confidence note near 0:52–0:55"]}),
("Greensleeves", "Traditional", "done",
{"lyrics": GREENSLEEVES_LYRICS,
"warnings": ["1 low-confidence note near 0:45–0:47",
"1 line dropped (not sung) near \"for i have loved\"",
"missing lyrics near 2:10–2:18"],
"review": {
"lines": [
{"index": 0, "text": "Alas, my love, you do me wrong",
"dropped": False,
"notes": [{"start": 12.0, "end": 12.4, "pitch": 0, "low_confidence": False},
{"start": 12.5, "end": 12.9, "pitch": 2, "low_confidence": False}]},
{"index": 1, "text": "To cast me off discourteously",
"dropped": False,
"notes": [{"start": 45.0, "end": 45.4, "pitch": 4, "low_confidence": True}]},
{"index": 2, "text": "For I have loved you well and long",
"dropped": True, "notes": []},
],
"missing_spans": [{"start": 130.0, "end": 138.0}],
}}),
("Such Great Heights", "The Postal Service", "failed", ("Such Great Heights", "The Postal Service", "failed",
{"error": "yt-dlp: unable to download video data: HTTP Error 403: Forbidden"}), {"error": "yt-dlp: unable to download video data: HTTP Error 403: Forbidden"}),
("Time Escaping", "Big Thief", "running", {}), ("Time Escaping", "Big Thief", "running", {}),
@@ -51,6 +97,22 @@ def make_runner():
if kind == "done": if kind == "done":
for s in STAGES: for s in STAGES:
log(f"[{s}] running") log(f"[{s}] running")
if "review" in extra:
work = cfg.work_dir / song_slug(inp)
work.mkdir(parents=True, exist_ok=True)
notes = [
{**n, "duration": n["end"] - n["start"], "text": "~",
"line_index": line["index"]}
for line in extra["review"]["lines"] for n in line["notes"]
]
(work / "notes_sec.json").write_text(json.dumps({
"notes": notes, "breaks": [], "warnings": extra["warnings"]}))
(work / "align_meta.json").write_text(json.dumps({
"dropped_lines": [line["index"] for line in extra["review"]["lines"]
if line["dropped"]],
"missing_spans": [[s["start"], s["end"]]
for s in extra["review"]["missing_spans"]],
}))
out = cfg.output_dir / f"{inp.artist} - {inp.title}" out = cfg.output_dir / f"{inp.artist} - {inp.title}"
out.mkdir(parents=True, exist_ok=True) out.mkdir(parents=True, exist_ok=True)
return PipelineResult(output_dir=out, warnings=extra["warnings"]) return PipelineResult(output_dir=out, warnings=extra["warnings"])
@@ -76,11 +138,11 @@ def start_server(tmp_path: Path) -> str:
def seed_jobs(base_url: str) -> None: def seed_jobs(base_url: str) -> None:
lyrics = "[verse]\n...\n[chorus]\n...\n"
with httpx.Client(timeout=10) as client: with httpx.Client(timeout=10) as client:
for title, artist, kind, _ in SONGS: for title, artist, kind, extra in SONGS:
resp = client.post(f"{base_url}/api/jobs", data={ resp = client.post(f"{base_url}/api/jobs", data={
"title": title, "artist": artist, "lyrics": lyrics, "title": title, "artist": artist,
"lyrics": extra.get("lyrics", DEFAULT_LYRICS),
"youtube_url": "https://youtu.be/00000000000"}) "youtube_url": "https://youtu.be/00000000000"})
resp.raise_for_status() resp.raise_for_status()
job_id = resp.json()["id"] job_id = resp.json()["id"]