diff --git a/worker/lyra_worker/adapters/_streamrip.py b/worker/lyra_worker/adapters/_streamrip.py index 69840c5..3192ca0 100644 --- a/worker/lyra_worker/adapters/_streamrip.py +++ b/worker/lyra_worker/adapters/_streamrip.py @@ -1,8 +1,14 @@ import asyncio +import hashlib import os from typing import Callable +def _hashed_password(password: str) -> str: + """streamrip's Qobuz email/password login expects the password as an MD5 hex digest.""" + return hashlib.md5(password.encode("utf-8")).hexdigest() + + class StreamripClient: """Real Qobuz client backed by streamrip (v2, async → sync via asyncio.run). @@ -23,7 +29,7 @@ class StreamripClient: cfg = Config.defaults() cfg.session.qobuz.use_auth_token = False cfg.session.qobuz.email_or_userid = self._email - cfg.session.qobuz.password_or_token = self._password + cfg.session.qobuz.password_or_token = _hashed_password(self._password) cfg.session.qobuz.quality = 3 if download_folder is not None: cfg.session.downloads.folder = download_folder @@ -37,7 +43,10 @@ class StreamripClient: client = QobuzClient(self._make_config()) try: - await client.login() + try: + await client.login() + except Exception as e: # never let streamrip's credential-bearing error escape + raise RuntimeError(f"Qobuz login failed ({type(e).__name__}) — check credentials") from None pages = await client.search("album", query, limit=5) finally: await client.session.close() @@ -68,7 +77,10 @@ class StreamripClient: cfg = self._make_config(download_folder=dest) client = QobuzClient(cfg) try: - await client.login() + try: + await client.login() + except Exception as e: # never let streamrip's credential-bearing error escape + raise RuntimeError(f"Qobuz login failed ({type(e).__name__}) — check credentials") from None on_progress(0.0) db = Database(downloads=Dummy(), failed=Dummy()) pending = PendingAlbum(album_id, client, cfg, db) diff --git a/worker/tests/test_streamrip_auth.py b/worker/tests/test_streamrip_auth.py new file mode 100644 index 0000000..11a9a3b --- /dev/null +++ b/worker/tests/test_streamrip_auth.py @@ -0,0 +1,15 @@ +import hashlib + +from lyra_worker.adapters._streamrip import StreamripClient, _hashed_password + + +def test_hashed_password_is_md5_hex(): + # streamrip's Qobuz email/password login expects the MD5 hex digest, not plaintext. + assert _hashed_password("hunter2") == hashlib.md5(b"hunter2").hexdigest() + assert len(_hashed_password("anything")) == 32 + assert all(c in "0123456789abcdef" for c in _hashed_password("anything")) + + +def test_is_configured(): + assert StreamripClient({"qobuz.email": "a@b.c", "qobuz.password": "p"}).is_configured() is True + assert StreamripClient({}).is_configured() is False