feat: organized album directory and persistent /music bind-mount

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan
2026-07-10 23:43:15 +02:00
parent 6cce95cec6
commit 2812695465
5 changed files with 50 additions and 1 deletions
+3
View File
@@ -5,3 +5,6 @@ DATABASE_URL=postgresql://lyra:lyra@db:5432/lyra
# 32 random bytes, base64. Generate with: openssl rand -base64 32 # 32 random bytes, base64. Generate with: openssl rand -base64 32
LYRA_SECRET_KEY=MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY= LYRA_SECRET_KEY=MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=
# Host directory for the downloaded music library (bind-mounted into the worker at /music)
MUSIC_DIR=./music
+2
View File
@@ -35,6 +35,8 @@ services:
environment: environment:
DATABASE_URL: ${DATABASE_URL} DATABASE_URL: ${DATABASE_URL}
LYRA_SECRET_KEY: ${LYRA_SECRET_KEY} LYRA_SECRET_KEY: ${LYRA_SECRET_KEY}
volumes:
- ${MUSIC_DIR:-./music}:/music
depends_on: depends_on:
db: db:
condition: service_healthy condition: service_healthy
+19
View File
@@ -0,0 +1,19 @@
import re
from lyra_worker.types import MBTarget
_ILLEGAL = re.compile(r'[<>:"/\\|?*\x00-\x1f]')
def safe_name(s: str) -> str:
"""A filesystem-safe path component (single segment, no separators)."""
cleaned = _ILLEGAL.sub("_", s).strip().strip(".").strip()
return cleaned or "Unknown"
def album_dir(dest_root: str, target: MBTarget) -> str:
"""`{dest_root}/{safe artist}/{safe album}[ (year)]`."""
album = safe_name(target.album)
if target.year:
album = f"{album} ({target.year})"
return f"{dest_root}/{safe_name(target.artist)}/{album}"
+2 -1
View File
@@ -5,6 +5,7 @@ import psycopg
from lyra_worker.adapters.base import SourceAdapter from lyra_worker.adapters.base import SourceAdapter
from lyra_worker.confidence import score_confidence from lyra_worker.confidence import score_confidence
from lyra_worker.library import album_dir
from lyra_worker.quality import quality_class from lyra_worker.quality import quality_class
from lyra_worker.ranker import rank_candidates from lyra_worker.ranker import rank_candidates
from lyra_worker.types import Candidate, MBTarget from lyra_worker.types import Candidate, MBTarget
@@ -150,7 +151,7 @@ def run_pipeline(
# 4. download (fall-through) # 4. download (fall-through)
_set_state(conn, job_id, "downloading", "download") _set_state(conn, job_id, "downloading", "download")
by_source = {a.name: a for a in adapters} by_source = {a.name: a for a in adapters}
dest = f"{dest_root}/{target.artist}/{target.album}" dest = album_dir(dest_root, target)
winner = None winner = None
result = None result = None
for candidate in ranked: for candidate in ranked:
+24
View File
@@ -0,0 +1,24 @@
from lyra_worker.library import album_dir, safe_name
from lyra_worker.types import MBTarget
def test_safe_name_replaces_illegal_chars():
assert safe_name("AC/DC") == "AC_DC"
assert safe_name('a:b*c?"') == "a_b_c__"
assert safe_name(" .Trailing. ") == "Trailing"
assert safe_name("") == "Unknown"
def test_album_dir_with_year():
t = MBTarget(artist="John Mayer", album="Continuum", year=2006)
assert album_dir("/music", t) == "/music/John Mayer/Continuum (2006)"
def test_album_dir_without_year():
t = MBTarget(artist="John Mayer", album="Continuum")
assert album_dir("/music", t) == "/music/John Mayer/Continuum"
def test_album_dir_sanitizes():
t = MBTarget(artist="AC/DC", album="Back in Black", year=1980)
assert album_dir("/music", t) == "/music/AC_DC/Back in Black (1980)"