mirror of
https://github.com/dbv111m/musicgrabber.git
synced 2026-10-21 10:39:48 -05:00
SeleniumBase off the leash (within 4.x), with a live Amazon test holding the other end
The pin froze SeleniumBase while Chrome updated itself every build, which is the combination most likely to break undetected mode. It now floats within 4.x (4.55.0 today), and test_amazon_album_names_itself names a real Amazon album through /api/albums/resolve-album-url, so a breakage fails the slow suite instead of somebody's album import. Built with a human and a grumpy-AI combo, trusting tests over pins.
This commit is contained in:
1 parent
a2b408e8c5
commit
aa220b82bb
5 files changed
+62
-4
No files matched your search
+1
-1
@@ -9,7 +9,7 @@ Housekeeping. The backlog's to-do list, cleared in one sitting, which has never
|
||||
|
||||
### Changed
|
||||
- **One spacing scale, actually used**: `style.css` had a perfectly good spacing scale that 30 of its 513 margins, paddings and gaps used; the rest were picked by eye, one spot at a time, which is how you get fourteen slightly different gaps. Every one is now on the scale, which gained half-steps for 2, 6, 10 and 20px because those were doing too much work to snap away. The rare odd values (3, 5, 7, 14, 18px and friends) moved to the nearest step, so a few badges, chips and cards shift by a pixel or two; nothing should look different unless you go looking with a ruler.
|
||||
- **SeleniumBase 4.49 to 4.54**: it only drives the Amazon Music album namer now, which was checked against real albums on the UK and US storefronts before trusting it.
|
||||
- **SeleniumBase unpinned within 4.x**: it only drives the Amazon Music album namer now, and the pin was guarding the wrong half. Chrome is always whatever is current when the image is built, and SeleniumBase is where the fixes for Chrome's changes turn up, so an old SeleniumBase against a new Chrome was the riskier pairing. Each build now takes the newest 4.x (4.55.0 today), and a new slow test names a real Amazon album through the API, so the test suite notices a breakage before your next album link does.
|
||||
- **Boot work waits for the server**: database setup, the schedulers and the source health sweep now run when uvicorn starts the app rather than whenever something imports `app.py`. Unit tests import it for its route functions and used to get the whole lot thrown in, including a health sweep probing every source over the real network mid-suite.
|
||||
- **The cache-bust token watches the stylesheet too**: it only looked at `app.js`, `index.html` and `release-notes.js`, so a change to `style.css` or `quips.js` alone could be served stale from the browser cache until something else changed.
|
||||
|
||||
|
||||
+5
-1
@@ -45,7 +45,11 @@ RUN pip install --no-cache-dir \
|
||||
# SeleniumBase is kept beside Playwright rather than replacing the working
|
||||
# Spotify/Amazon playlist scrapers. Its undetected mode is what gets an Amazon
|
||||
# Music album page to name itself (amazonalbum.py); Playwright cannot.
|
||||
RUN pip install --no-cache-dir seleniumbase~=4.54.0
|
||||
# It floats within 4.x on purpose: Chrome below is whatever is current at build
|
||||
# time, and SeleniumBase is where the fixes for Chrome's changes arrive. A pin
|
||||
# only froze the half that does not break. test_amazon_album_names_itself (slow)
|
||||
# is the safety net; <5 keeps out a major rewrite.
|
||||
RUN pip install --no-cache-dir "seleniumbase>=4.54,<5"
|
||||
|
||||
# Install Playwright browsers into a fixed path so non-root users (PUID/PGID) can find them.
|
||||
# Without this, Playwright falls back to ~/.cache/ms-playwright which resolves differently per user.
|
||||
|
||||
@@ -5,6 +5,7 @@ All are @pytest.mark.slow - they hit real external services.
|
||||
Current state (2026-09-22):
|
||||
- Spotify embed: BROKEN (returns 1 track) - tests correctly catch this
|
||||
- Amazon: working (showHome JSON with rendered-page fallback)
|
||||
- Amazon albums: working (SeleniumBase undetected mode, page title)
|
||||
- Apple Music: working
|
||||
- YouTube: working (see test_playlists.py)
|
||||
- ListenBrainz: working
|
||||
@@ -158,6 +159,31 @@ def test_fetch_amazon_playlist_over_one_hundred_tracks(api, base_url):
|
||||
assert len(d.get("tracks", [])) > 100
|
||||
|
||||
|
||||
# Album pages are named by amazonalbum.py driving SeleniumBase's undetected
|
||||
# mode, and SeleniumBase floats within 4.x while Chrome is whatever is current
|
||||
# at build time. This is the test that notices when one of them, or Amazon,
|
||||
# moves the goalposts, rather than the next person to paste an album link.
|
||||
_AMAZON_ALBUM_URL = "https://music.amazon.co.uk/albums/B0FBGRYDM9" # Olivia Dean, The Art of Loving
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_amazon_album_names_itself(api, base_url):
|
||||
r = api.post(
|
||||
f"{base_url}/api/albums/resolve-album-url",
|
||||
json={"url": _AMAZON_ALBUM_URL},
|
||||
timeout=150,
|
||||
)
|
||||
if r.status_code == 503:
|
||||
pytest.skip(f"MusicBrainz unreachable, which says nothing about Amazon: {r.text}")
|
||||
assert r.status_code == 200, f"resolve failed ({r.status_code}): {r.text}"
|
||||
d = r.json()
|
||||
assert d.get("service") == "amazon"
|
||||
assert (d.get("artist"), d.get("album")) == ("Olivia Dean", "The Art of Loving"), (
|
||||
"Amazon did not name the album; check SeleniumBase, Chrome and "
|
||||
f"amazonalbum.py against the container logs. Got: {d}"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ListenBrainz
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -118,6 +118,28 @@ def test_check_duplicate_falls_back_to_primary_credited_artist(tmp_path, monkeyp
|
||||
)
|
||||
|
||||
|
||||
def test_check_duplicate_does_not_treat_instrumental_as_original(tmp_path, monkeypatch):
|
||||
"""A version qualifier must not make a different recording a duplicate."""
|
||||
import utils
|
||||
|
||||
singles = tmp_path / "Singles"
|
||||
instrumental = singles / "Michael Jackson - Thriller (Instrumental).flac"
|
||||
instrumental.parent.mkdir(parents=True)
|
||||
instrumental.write_bytes(b"not the original")
|
||||
|
||||
monkeypatch.setattr(utils, "get_download_dir", lambda *a, **kw: singles)
|
||||
monkeypatch.setattr(utils, "get_singles_dir", lambda **kw: singles)
|
||||
monkeypatch.setattr(utils, "get_albums_dir", lambda **kw: tmp_path / "Albums")
|
||||
monkeypatch.setattr(utils, "get_playlists_dir", lambda **kw: None)
|
||||
monkeypatch.setattr(utils, "_tagged_duplicate", lambda *a, **kw: None)
|
||||
|
||||
assert utils.check_duplicate("Michael Jackson", "Thriller") is None
|
||||
assert (
|
||||
utils.check_duplicate("Michael Jackson", "Thriller (Instrumental)")
|
||||
== instrumental
|
||||
)
|
||||
|
||||
|
||||
def test_check_duplicate_finds_externally_renamed_file_by_tags(tmp_path, monkeypatch):
|
||||
import metadata
|
||||
import time
|
||||
|
||||
@@ -159,10 +159,16 @@ def clean_title(title: str) -> str:
|
||||
|
||||
|
||||
def _normalise_duplicate_stem(stem: str) -> str:
|
||||
"""Normalise a filename stem for duplicate comparisons."""
|
||||
"""Normalise a filename stem for duplicate comparisons.
|
||||
|
||||
Keep bracketed version details. They are part of a recording's identity:
|
||||
treating ``Thriller (Instrumental)`` as ``Thriller`` means the instrumental
|
||||
can prevent the actual song being downloaded. ``clean_title`` has already
|
||||
removed disposable publishing noise such as "Official Video" before this
|
||||
point, so retaining the remaining words is the safer choice.
|
||||
"""
|
||||
s = clean_title(stem or "")
|
||||
s = s.replace("’", "'").replace("‘", "'").replace("`", "'")
|
||||
s = re.sub(r"\s*[\(\[].*?[\)\]]", "", s)
|
||||
s = re.sub(r"[^a-z0-9]+", " ", s.lower())
|
||||
return re.sub(r"\s+", " ", s).strip()
|
||||
|
||||
|
||||
Reference in new issue
Block a user