Merge pull request 'Rename project from karaoke-maker to songtrace' (#3) from songtrace-rename into main
Reviewed-on: branderson/karaoke-maker#3
This commit is contained in:
+2
-2
@@ -11,9 +11,9 @@ COPY pyproject.toml ./
|
|||||||
COPY src ./src
|
COPY src ./src
|
||||||
RUN pip install --no-cache-dir .
|
RUN pip install --no-cache-dir .
|
||||||
|
|
||||||
ENV KARAOKE_MAKER_CONFIG=/config/config.toml \
|
ENV SONGTRACE_CONFIG=/config/config.toml \
|
||||||
TORCH_HOME=/data/models \
|
TORCH_HOME=/data/models \
|
||||||
XDG_CACHE_HOME=/data/models/cache
|
XDG_CACHE_HOME=/data/models/cache
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
CMD ["karaoke-maker", "serve"]
|
CMD ["songtrace", "serve"]
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
# karaoke-maker
|
# songtrace
|
||||||
|
|
||||||
|
Turn a song and its lyrics into a playable UltraStar chart.
|
||||||
|
|
||||||
A CLI and minimal web UI that turns a song — title, artist, lyrics you supply,
|
A CLI and minimal web UI that turns a song — title, artist, lyrics you supply,
|
||||||
and a YouTube URL or local video file — into a playable
|
and a YouTube URL or local video file — into a playable
|
||||||
@@ -35,8 +37,21 @@ editor such as [Yass](http://www.yass-hq.de/) or
|
|||||||
Config is TOML. Resolution order:
|
Config is TOML. Resolution order:
|
||||||
|
|
||||||
1. `--config <path>` passed explicitly on the command line
|
1. `--config <path>` passed explicitly on the command line
|
||||||
2. the `KARAOKE_MAKER_CONFIG` environment variable
|
2. the `SONGTRACE_CONFIG` environment variable
|
||||||
3. `~/.config/karaoke-maker/config.toml`
|
3. `~/.config/songtrace/config.toml`
|
||||||
|
|
||||||
|
If you used this project under its former name, move your existing config once:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip uninstall karaoke-maker # removes the old console script
|
||||||
|
mv ~/.config/karaoke-maker ~/.config/songtrace
|
||||||
|
```
|
||||||
|
|
||||||
|
The Docker deployment needs no config migration — the image sets
|
||||||
|
`SONGTRACE_CONFIG` itself and the config is bind-mounted to a fixed path.
|
||||||
|
Run `docker compose down` before upgrading (or `docker compose up -d
|
||||||
|
--remove-orphans`) since the service key changed and Compose won't replace
|
||||||
|
the old container in place.
|
||||||
|
|
||||||
Copy the example and fill in your Whisper server:
|
Copy the example and fill in your Whisper server:
|
||||||
|
|
||||||
@@ -73,7 +88,7 @@ container recreation.
|
|||||||
Run the CLI inside the container instead of the web UI:
|
Run the CLI inside the container instead of the web UI:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker compose run --rm karaoke-maker karaoke-maker make \
|
docker compose run --rm songtrace songtrace make \
|
||||||
--title "Song Title" --artist "Artist Name" \
|
--title "Song Title" --artist "Artist Name" \
|
||||||
--lyrics /data/lyrics.txt --youtube https://youtu.be/...
|
--lyrics /data/lyrics.txt --youtube https://youtu.be/...
|
||||||
```
|
```
|
||||||
@@ -87,17 +102,17 @@ installed on the host; `docker-compose.yml` requests one GPU via the
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
pip install -e .
|
pip install -e .
|
||||||
karaoke-maker make --help
|
songtrace make --help
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
karaoke-maker make --title TITLE --artist ARTIST --lyrics LYRICS_FILE \
|
songtrace make --title TITLE --artist ARTIST --lyrics LYRICS_FILE \
|
||||||
(--youtube URL | --video PATH) \
|
(--youtube URL | --video PATH) \
|
||||||
[--genre GENRE] [--year YEAR] [--language LANGUAGE] [--cover COVER_IMAGE] \
|
[--genre GENRE] [--year YEAR] [--language LANGUAGE] [--cover COVER_IMAGE] \
|
||||||
[--bpm BPM] [--force-from STAGE] [--config CONFIG]
|
[--bpm BPM] [--force-from STAGE] [--config CONFIG]
|
||||||
```
|
```
|
||||||
|
|
||||||
Run the web UI instead with `karaoke-maker serve [--config CONFIG]`.
|
Run the web UI instead with `songtrace serve [--config CONFIG]`.
|
||||||
|
|
||||||
## Re-running a stage
|
## Re-running a stage
|
||||||
|
|
||||||
@@ -108,7 +123,7 @@ stage and everything after it, reusing the cached output of everything
|
|||||||
before it:
|
before it:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
karaoke-maker make --title "..." --artist "..." --lyrics lyrics.txt \
|
songtrace make --title "..." --artist "..." --lyrics lyrics.txt \
|
||||||
--youtube https://youtu.be/... --force-from align
|
--youtube https://youtu.be/... --force-from align
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
services:
|
services:
|
||||||
karaoke-maker:
|
songtrace:
|
||||||
build: .
|
build: .
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "karaoke-maker"
|
name = "songtrace"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Generate UltraStar/Melody Mania karaoke songs from a video and reference lyrics"
|
description = "Generate UltraStar/Melody Mania karaoke songs from a video and reference lyrics"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
@@ -26,7 +26,7 @@ dependencies = [
|
|||||||
dev = ["pytest", "playwright"]
|
dev = ["pytest", "playwright"]
|
||||||
|
|
||||||
[project.scripts]
|
[project.scripts]
|
||||||
karaoke-maker = "karaoke_maker.cli:main"
|
songtrace = "songtrace.cli:main"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools>=68"]
|
requires = ["setuptools>=68"]
|
||||||
@@ -36,7 +36,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
where = ["src"]
|
where = ["src"]
|
||||||
|
|
||||||
[tool.setuptools.package-data]
|
[tool.setuptools.package-data]
|
||||||
karaoke_maker = ["static/*", "static/fonts/*"]
|
songtrace = ["static/*", "static/fonts/*"]
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
testpaths = ["tests"]
|
testpaths = ["tests"]
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ import httpx
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
from playwright.sync_api import sync_playwright
|
from playwright.sync_api import sync_playwright
|
||||||
|
|
||||||
from karaoke_maker.config import Config
|
from songtrace.config import Config
|
||||||
from karaoke_maker.pipeline import PipelineError, PipelineResult
|
from songtrace.pipeline import PipelineError, PipelineResult
|
||||||
from karaoke_maker.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"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ def _positive_float(value: str) -> float:
|
|||||||
|
|
||||||
|
|
||||||
def build_parser() -> argparse.ArgumentParser:
|
def build_parser() -> argparse.ArgumentParser:
|
||||||
parser = argparse.ArgumentParser(prog="karaoke-maker")
|
parser = argparse.ArgumentParser(prog="songtrace")
|
||||||
parser.add_argument("--version", action="store_true")
|
parser.add_argument("--version", action="store_true")
|
||||||
sub = parser.add_subparsers(dest="command")
|
sub = parser.add_subparsers(dest="command")
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ def _cmd_make(args) -> int:
|
|||||||
def main(argv: list[str] | None = None) -> int:
|
def main(argv: list[str] | None = None) -> int:
|
||||||
args = build_parser().parse_args(argv)
|
args = build_parser().parse_args(argv)
|
||||||
if args.version:
|
if args.version:
|
||||||
print(f"karaoke-maker {version('karaoke-maker')}")
|
print(f"songtrace {version('songtrace')}")
|
||||||
return 0
|
return 0
|
||||||
if args.command == "make":
|
if args.command == "make":
|
||||||
return _cmd_make(args)
|
return _cmd_make(args)
|
||||||
@@ -12,7 +12,7 @@ class Config:
|
|||||||
whisper_api: str = "openai"
|
whisper_api: str = "openai"
|
||||||
output_dir: Path = Path("output")
|
output_dir: Path = Path("output")
|
||||||
work_dir: Path = Path("work")
|
work_dir: Path = Path("work")
|
||||||
creator: str = "karaoke-maker"
|
creator: str = "songtrace"
|
||||||
demucs_model: str = "htdemucs_ft"
|
demucs_model: str = "htdemucs_ft"
|
||||||
crepe_model: str = "full"
|
crepe_model: str = "full"
|
||||||
device: str = "cuda"
|
device: str = "cuda"
|
||||||
@@ -25,8 +25,8 @@ _PATH_FIELDS = {"output_dir", "work_dir"}
|
|||||||
|
|
||||||
def load_config(path: Path | None = None) -> Config:
|
def load_config(path: Path | None = None) -> Config:
|
||||||
if path is None:
|
if path is None:
|
||||||
env = os.environ.get("KARAOKE_MAKER_CONFIG")
|
env = os.environ.get("SONGTRACE_CONFIG")
|
||||||
path = Path(env) if env else Path.home() / ".config/karaoke-maker/config.toml"
|
path = Path(env) if env else Path.home() / ".config/songtrace/config.toml"
|
||||||
data = tomllib.loads(Path(path).read_text())
|
data = tomllib.loads(Path(path).read_text())
|
||||||
known = {f.name for f in fields(Config)}
|
known = {f.name for f in fields(Config)}
|
||||||
unknown = set(data) - known
|
unknown = set(data) - known
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="color-scheme" content="dark">
|
<meta name="color-scheme" content="dark">
|
||||||
<title>karaoke-maker</title>
|
<title>songtrace</title>
|
||||||
<style>
|
<style>
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Chakra Petch";
|
font-family: "Chakra Petch";
|
||||||
@@ -473,7 +473,7 @@ button { cursor: pointer; }
|
|||||||
|
|
||||||
<aside id="sidebar">
|
<aside id="sidebar">
|
||||||
<div class="brand">
|
<div class="brand">
|
||||||
<h1 class="wordmark"><span>KARAOKE</span> <span class="second">MAKER</span></h1>
|
<h1 class="wordmark"><span>SONG</span> <span class="second">TRACE</span></h1>
|
||||||
<div class="kana" aria-hidden="true">カラオケ</div>
|
<div class="kana" aria-hidden="true">カラオケ</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from karaoke_maker.config import Config
|
from songtrace.config import Config
|
||||||
from karaoke_maker.pipeline import Ctx, SongInput
|
from songtrace.pipeline import Ctx, SongInput
|
||||||
from karaoke_maker.stages import acquire
|
from songtrace.stages import acquire
|
||||||
|
|
||||||
def make_ctx(tmp_path, **kw):
|
def make_ctx(tmp_path, **kw):
|
||||||
inp = SongInput(title="T", artist="A", lyrics_text="x", **kw)
|
inp = SongInput(title="T", artist="A", lyrics_text="x", **kw)
|
||||||
@@ -46,7 +46,7 @@ def test_outputs_are_meta_and_audio(tmp_path):
|
|||||||
|
|
||||||
def test_requires_exactly_one_source(tmp_path):
|
def test_requires_exactly_one_source(tmp_path):
|
||||||
import pytest
|
import pytest
|
||||||
from karaoke_maker.pipeline import PipelineError
|
from songtrace.pipeline import PipelineError
|
||||||
ctx = make_ctx(tmp_path)
|
ctx = make_ctx(tmp_path)
|
||||||
with pytest.raises(PipelineError):
|
with pytest.raises(PipelineError):
|
||||||
acquire.acquire_stage().run(ctx)
|
acquire.acquire_stage().run(ctx)
|
||||||
@@ -73,7 +73,7 @@ def test_classify_no_thumbnail(tmp_path):
|
|||||||
|
|
||||||
def test_classify_no_video_raises(tmp_path):
|
def test_classify_no_video_raises(tmp_path):
|
||||||
import pytest
|
import pytest
|
||||||
from karaoke_maker.pipeline import PipelineError
|
from songtrace.pipeline import PipelineError
|
||||||
(tmp_path / "source_video.jpg").write_bytes(b"t")
|
(tmp_path / "source_video.jpg").write_bytes(b"t")
|
||||||
with pytest.raises(PipelineError):
|
with pytest.raises(PipelineError):
|
||||||
acquire._classify_downloads(tmp_path)
|
acquire._classify_downloads(tmp_path)
|
||||||
|
|||||||
+4
-4
@@ -2,10 +2,10 @@ import json
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from karaoke_maker.config import Config
|
from songtrace.config import Config
|
||||||
from karaoke_maker.lyrics import normalize_lyrics
|
from songtrace.lyrics import normalize_lyrics
|
||||||
from karaoke_maker.pipeline import Ctx, PipelineError, SongInput
|
from songtrace.pipeline import Ctx, PipelineError, SongInput
|
||||||
from karaoke_maker.stages import align
|
from songtrace.stages import align
|
||||||
|
|
||||||
LYRICS = "it's one two three\nfour five six seven eight"
|
LYRICS = "it's one two three\nfour five six seven eight"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import soundfile as sf
|
import soundfile as sf
|
||||||
|
|
||||||
from karaoke_maker.audio_io import load_wav
|
from songtrace.audio_io import load_wav
|
||||||
|
|
||||||
|
|
||||||
def test_load_wav_mono(tmp_path):
|
def test_load_wav_mono(tmp_path):
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
from karaoke_maker.cli import main
|
from songtrace.cli import main
|
||||||
|
|
||||||
def test_version(capsys):
|
def test_version(capsys):
|
||||||
assert main(["--version"]) == 0
|
assert main(["--version"]) == 0
|
||||||
assert "karaoke-maker" in capsys.readouterr().out
|
assert "songtrace" in capsys.readouterr().out
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import os
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import karaoke_maker.cli as cli
|
import songtrace.cli as cli
|
||||||
from karaoke_maker.config import Config
|
from songtrace.config import Config
|
||||||
from karaoke_maker.pipeline import PipelineError, PipelineResult
|
from songtrace.pipeline import PipelineError, PipelineResult
|
||||||
|
|
||||||
|
|
||||||
def setup_cfg(monkeypatch, tmp_path):
|
def setup_cfg(monkeypatch, tmp_path):
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from karaoke_maker.config import Config, load_config
|
from songtrace.config import Config, load_config
|
||||||
|
|
||||||
|
|
||||||
def test_load_minimal(tmp_path):
|
def test_load_minimal(tmp_path):
|
||||||
@@ -24,7 +24,7 @@ def test_paths_and_overrides(tmp_path):
|
|||||||
def test_env_var(tmp_path, monkeypatch):
|
def test_env_var(tmp_path, monkeypatch):
|
||||||
cfg_file = tmp_path / "c.toml"
|
cfg_file = tmp_path / "c.toml"
|
||||||
cfg_file.write_text('whisper_url = "http://env/v1"\n')
|
cfg_file.write_text('whisper_url = "http://env/v1"\n')
|
||||||
monkeypatch.setenv("KARAOKE_MAKER_CONFIG", str(cfg_file))
|
monkeypatch.setenv("SONGTRACE_CONFIG", str(cfg_file))
|
||||||
assert load_config().whisper_url == "http://env/v1"
|
assert load_config().whisper_url == "http://env/v1"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+9
-9
@@ -4,27 +4,27 @@ import shutil
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
requires_full = pytest.mark.skipif(
|
requires_full = pytest.mark.skipif(
|
||||||
not os.environ.get("KARAOKE_E2E"),
|
not os.environ.get("SONGTRACE_E2E"),
|
||||||
reason="set KARAOKE_E2E=1 (needs GPU, ffmpeg, whisper server, network)")
|
reason="set SONGTRACE_E2E=1 (needs GPU, ffmpeg, whisper server, network)")
|
||||||
|
|
||||||
@requires_full
|
@requires_full
|
||||||
def test_full_pipeline(tmp_path):
|
def test_full_pipeline(tmp_path):
|
||||||
from karaoke_maker.config import load_config
|
from songtrace.config import load_config
|
||||||
from karaoke_maker.pipeline import SongInput, run_pipeline
|
from songtrace.pipeline import SongInput, run_pipeline
|
||||||
assert shutil.which("ffmpeg"), "ffmpeg required"
|
assert shutil.which("ffmpeg"), "ffmpeg required"
|
||||||
cfg = load_config()
|
cfg = load_config()
|
||||||
cfg.output_dir = tmp_path / "out"
|
cfg.output_dir = tmp_path / "out"
|
||||||
cfg.work_dir = tmp_path / "work"
|
cfg.work_dir = tmp_path / "work"
|
||||||
inp = SongInput(
|
inp = SongInput(
|
||||||
title=os.environ["KARAOKE_E2E_TITLE"],
|
title=os.environ["SONGTRACE_E2E_TITLE"],
|
||||||
artist=os.environ["KARAOKE_E2E_ARTIST"],
|
artist=os.environ["SONGTRACE_E2E_ARTIST"],
|
||||||
lyrics_text=open(os.environ["KARAOKE_E2E_LYRICS"]).read(),
|
lyrics_text=open(os.environ["SONGTRACE_E2E_LYRICS"]).read(),
|
||||||
youtube_url=os.environ.get("KARAOKE_E2E_URL"),
|
youtube_url=os.environ.get("SONGTRACE_E2E_URL"),
|
||||||
)
|
)
|
||||||
result = run_pipeline(inp, cfg)
|
result = run_pipeline(inp, cfg)
|
||||||
txts = list(result.output_dir.glob("*.txt"))
|
txts = list(result.output_dir.glob("*.txt"))
|
||||||
assert len(txts) == 1
|
assert len(txts) == 1
|
||||||
from karaoke_maker.ultrastar import get_header, parse_ultrastar
|
from songtrace.ultrastar import get_header, parse_ultrastar
|
||||||
song = parse_ultrastar(txts[0].read_text(encoding="utf-8"))
|
song = parse_ultrastar(txts[0].read_text(encoding="utf-8"))
|
||||||
assert get_header(song, "BPM") and get_header(song, "GAP")
|
assert get_header(song, "BPM") and get_header(song, "GAP")
|
||||||
assert len(song.body) > 20
|
assert len(song.body) > 20
|
||||||
|
|||||||
+5
-5
@@ -2,11 +2,11 @@ import json
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pytest
|
import pytest
|
||||||
from karaoke_maker.config import Config
|
from songtrace.config import Config
|
||||||
from karaoke_maker.grid import scale_bpm, to_ultrastar
|
from songtrace.grid import scale_bpm, to_ultrastar
|
||||||
from karaoke_maker.pipeline import Ctx, PipelineError, SongInput
|
from songtrace.pipeline import Ctx, PipelineError, SongInput
|
||||||
from karaoke_maker.stages import all_stages, grid as grid_stage_mod
|
from songtrace.stages import all_stages, grid as grid_stage_mod
|
||||||
from karaoke_maker.ultrastar import (
|
from songtrace.ultrastar import (
|
||||||
LineBreak, Note, get_header, parse_ultrastar,
|
LineBreak, Note, get_header, parse_ultrastar,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from karaoke_maker.lyrics import normalize_lyrics, alignment_token
|
from songtrace.lyrics import normalize_lyrics, alignment_token
|
||||||
|
|
||||||
RAW = """[Verse 1]
|
RAW = """[Verse 1]
|
||||||
It's one two three
|
It's one two three
|
||||||
|
|||||||
+4
-4
@@ -2,13 +2,13 @@ import json
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pytest
|
import pytest
|
||||||
from karaoke_maker.config import Config
|
from songtrace.config import Config
|
||||||
from karaoke_maker.notes import (
|
from songtrace.notes import (
|
||||||
MIN_NOTE_SEC, TimedNote, build_notes, hz_to_ultrastar, segment_span,
|
MIN_NOTE_SEC, TimedNote, build_notes, hz_to_ultrastar, segment_span,
|
||||||
syllabify,
|
syllabify,
|
||||||
)
|
)
|
||||||
from karaoke_maker.pipeline import Ctx, SongInput
|
from songtrace.pipeline import Ctx, SongInput
|
||||||
from karaoke_maker.stages import all_stages, notes as notes_stage_mod
|
from songtrace.stages import all_stages, notes as notes_stage_mod
|
||||||
|
|
||||||
|
|
||||||
def flat_pitch(duration=10.0, hz=261.63): # C4
|
def flat_pitch(duration=10.0, hz=261.63): # C4
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ from pathlib import Path
|
|||||||
import pytest
|
import pytest
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from karaoke_maker.config import Config
|
from songtrace.config import Config
|
||||||
from karaoke_maker.pipeline import (
|
from songtrace.pipeline import (
|
||||||
STAGE_ORDER, Ctx, PipelineError, SongInput, run_pipeline, song_slug,
|
STAGE_ORDER, Ctx, PipelineError, SongInput, run_pipeline, song_slug,
|
||||||
)
|
)
|
||||||
from karaoke_maker.stages import all_stages, package
|
from songtrace.stages import all_stages, package
|
||||||
from karaoke_maker.ultrastar import get_header, parse_ultrastar
|
from songtrace.ultrastar import get_header, parse_ultrastar
|
||||||
|
|
||||||
SONG_TXT = ("#TITLE:T\n#ARTIST:A\n#MP3:A - T.mp3\n#COVER:A - T.jpg\n"
|
SONG_TXT = ("#TITLE:T\n#ARTIST:A\n#MP3:A - T.mp3\n#COVER:A - T.jpg\n"
|
||||||
"#VIDEO:A - T.webm\n#BPM:200\n#GAP:0\n: 0 1 0 la\nE\n")
|
"#VIDEO:A - T.webm\n#BPM:200\n#GAP:0\n: 0 1 0 la\nE\n")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from karaoke_maker.config import Config
|
from songtrace.config import Config
|
||||||
from karaoke_maker.pipeline import SongInput, Stage, run_pipeline, song_slug
|
from songtrace.pipeline import SongInput, Stage, run_pipeline, song_slug
|
||||||
|
|
||||||
def make_cfg(tmp_path):
|
def make_cfg(tmp_path):
|
||||||
return Config(whisper_url="http://x/v1", output_dir=tmp_path / "out",
|
return Config(whisper_url="http://x/v1", output_dir=tmp_path / "out",
|
||||||
|
|||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from karaoke_maker.config import Config
|
from songtrace.config import Config
|
||||||
from karaoke_maker.pipeline import Ctx, SongInput
|
from songtrace.pipeline import Ctx, SongInput
|
||||||
from karaoke_maker.stages import pitch
|
from songtrace.stages import pitch
|
||||||
|
|
||||||
|
|
||||||
def test_pitch_stage_writes_npz(tmp_path, monkeypatch):
|
def test_pitch_stage_writes_npz(tmp_path, monkeypatch):
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ from pathlib import Path
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from karaoke_maker.config import Config
|
from songtrace.config import Config
|
||||||
from karaoke_maker.pipeline import STAGE_ORDER, PipelineError, SongInput
|
from songtrace.pipeline import STAGE_ORDER, PipelineError, SongInput
|
||||||
from karaoke_maker.rebuild import (FIELD_STAGE, changed_fields, plan,
|
from songtrace.rebuild import (FIELD_STAGE, changed_fields, plan,
|
||||||
relocate_work, superseded_output)
|
relocate_work, superseded_output)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import soundfile as sf
|
import soundfile as sf
|
||||||
import torch
|
import torch
|
||||||
from karaoke_maker.config import Config
|
from songtrace.config import Config
|
||||||
from karaoke_maker.pipeline import Ctx, SongInput
|
from songtrace.pipeline import Ctx, SongInput
|
||||||
from karaoke_maker.stages import separate
|
from songtrace.stages import separate
|
||||||
|
|
||||||
|
|
||||||
def test_separate_writes_stems(tmp_path, monkeypatch):
|
def test_separate_writes_stems(tmp_path, monkeypatch):
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from karaoke_maker.lyrics import normalize_lyrics
|
from songtrace.lyrics import normalize_lyrics
|
||||||
from karaoke_maker.whisper_client import TranscriptSegment
|
from songtrace.whisper_client import TranscriptSegment
|
||||||
from karaoke_maker.stages.transcribe import lyrics_similarity, vocal_sections
|
from songtrace.stages.transcribe import lyrics_similarity, vocal_sections
|
||||||
|
|
||||||
def seg(s, e, t=""):
|
def seg(s, e, t=""):
|
||||||
return TranscriptSegment(start=s, end=e, text=t)
|
return TranscriptSegment(start=s, end=e, text=t)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from karaoke_maker.ultrastar import (
|
from songtrace.ultrastar import (
|
||||||
Note, LineBreak, parse_ultrastar, serialize_ultrastar,
|
Note, LineBreak, parse_ultrastar, serialize_ultrastar,
|
||||||
get_header, format_bpm, parse_bpm,
|
get_header, format_bpm, parse_bpm,
|
||||||
)
|
)
|
||||||
|
|||||||
+8
-8
@@ -6,9 +6,9 @@ import time
|
|||||||
import pytest
|
import pytest
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
from karaoke_maker.config import Config
|
from songtrace.config import Config
|
||||||
from karaoke_maker.pipeline import STAGE_ORDER, PipelineError, PipelineResult, song_slug
|
from songtrace.pipeline import STAGE_ORDER, PipelineError, PipelineResult, song_slug
|
||||||
from karaoke_maker.web import STAGE_LINE, create_app
|
from songtrace.web import STAGE_LINE, create_app
|
||||||
|
|
||||||
|
|
||||||
def make_client(tmp_path, runner):
|
def make_client(tmp_path, runner):
|
||||||
@@ -190,7 +190,7 @@ def test_cover_upload_kept_after_success(tmp_path):
|
|||||||
|
|
||||||
def test_cleanup_uploads_removes_an_inherited_upload_dir(tmp_path):
|
def test_cleanup_uploads_removes_an_inherited_upload_dir(tmp_path):
|
||||||
"""A rebuild's video may live under its parent's id, not its own."""
|
"""A rebuild's video may live under its parent's id, not its own."""
|
||||||
from karaoke_maker.web import _cleanup_uploads
|
from songtrace.web import _cleanup_uploads
|
||||||
cfg = Config(whisper_url="u", work_dir=tmp_path / "work",
|
cfg = Config(whisper_url="u", work_dir=tmp_path / "work",
|
||||||
output_dir=tmp_path / "out")
|
output_dir=tmp_path / "out")
|
||||||
parent = cfg.work_dir / "uploads" / "parent"
|
parent = cfg.work_dir / "uploads" / "parent"
|
||||||
@@ -201,7 +201,7 @@ def test_cleanup_uploads_removes_an_inherited_upload_dir(tmp_path):
|
|||||||
|
|
||||||
|
|
||||||
def test_cleanup_uploads_ignores_paths_outside_the_upload_root(tmp_path):
|
def test_cleanup_uploads_ignores_paths_outside_the_upload_root(tmp_path):
|
||||||
from karaoke_maker.web import _cleanup_uploads
|
from songtrace.web import _cleanup_uploads
|
||||||
cfg = Config(whisper_url="u", work_dir=tmp_path / "work",
|
cfg = Config(whisper_url="u", work_dir=tmp_path / "work",
|
||||||
output_dir=tmp_path / "out")
|
output_dir=tmp_path / "out")
|
||||||
outside = tmp_path / "somewhere" / "keep.mp4"
|
outside = tmp_path / "somewhere" / "keep.mp4"
|
||||||
@@ -212,7 +212,7 @@ def test_cleanup_uploads_ignores_paths_outside_the_upload_root(tmp_path):
|
|||||||
|
|
||||||
|
|
||||||
def test_cleanup_uploads_tolerates_a_missing_file(tmp_path):
|
def test_cleanup_uploads_tolerates_a_missing_file(tmp_path):
|
||||||
from karaoke_maker.web import _cleanup_uploads
|
from songtrace.web import _cleanup_uploads
|
||||||
cfg = Config(whisper_url="u", work_dir=tmp_path / "work",
|
cfg = Config(whisper_url="u", work_dir=tmp_path / "work",
|
||||||
output_dir=tmp_path / "out")
|
output_dir=tmp_path / "out")
|
||||||
_cleanup_uploads(cfg, "child", cfg.work_dir / "uploads" / "child" / "gone.mp4")
|
_cleanup_uploads(cfg, "child", cfg.work_dir / "uploads" / "child" / "gone.mp4")
|
||||||
@@ -340,13 +340,13 @@ def test_fonts_are_served(tmp_path):
|
|||||||
|
|
||||||
|
|
||||||
def test_font_licence_is_bundled():
|
def test_font_licence_is_bundled():
|
||||||
from karaoke_maker.web import STATIC
|
from songtrace.web import STATIC
|
||||||
licence = (STATIC / "fonts" / "OFL.txt").read_text(encoding="utf-8", errors="replace")
|
licence = (STATIC / "fonts" / "OFL.txt").read_text(encoding="utf-8", errors="replace")
|
||||||
assert "SIL OPEN FONT LICENSE" in licence.upper()
|
assert "SIL OPEN FONT LICENSE" in licence.upper()
|
||||||
|
|
||||||
|
|
||||||
def _index_html():
|
def _index_html():
|
||||||
from karaoke_maker.web import STATIC
|
from songtrace.web import STATIC
|
||||||
return (STATIC / "index.html").read_text(encoding="utf-8")
|
return (STATIC / "index.html").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import httpx
|
import httpx
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from karaoke_maker.config import Config
|
from songtrace.config import Config
|
||||||
from karaoke_maker.whisper_client import TranscriptSegment, transcribe
|
from songtrace.whisper_client import TranscriptSegment, transcribe
|
||||||
|
|
||||||
def test_transcribe_parses_segments(tmp_path):
|
def test_transcribe_parses_segments(tmp_path):
|
||||||
audio = tmp_path / "v.wav"
|
audio = tmp_path / "v.wav"
|
||||||
|
|||||||
Reference in New Issue
Block a user