Skip wishlist adds for manual library matches
This commit is contained in:
parent
3e7eeb7c9c
commit
0345478361
4 changed files with 96 additions and 1 deletions
|
|
@ -36,7 +36,10 @@ def get_match(
|
||||||
server_source: str = "",
|
server_source: str = "",
|
||||||
) -> Optional[dict]:
|
) -> Optional[dict]:
|
||||||
"""Return match row dict or None if not found."""
|
"""Return match row dict or None if not found."""
|
||||||
return db.get_manual_library_match(profile_id, source, source_track_id, server_source)
|
getter = getattr(db, "get_manual_library_match", None)
|
||||||
|
if getter is None:
|
||||||
|
return None
|
||||||
|
return getter(profile_id, source, source_track_id, server_source)
|
||||||
|
|
||||||
|
|
||||||
def delete_match(db, match_id: int, profile_id: int) -> bool:
|
def delete_match(db, match_id: int, profile_id: int) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -7217,6 +7217,16 @@ class MusicDatabase:
|
||||||
logger.error("Cannot add track to wishlist: missing track ID")
|
logger.error("Cannot add track to wishlist: missing track ID")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
track_source = spotify_track_data.get('provider') or spotify_track_data.get('source') or 'spotify'
|
||||||
|
if self.get_manual_library_match(profile_id, track_source, track_id):
|
||||||
|
logger.info(
|
||||||
|
"Skipping wishlist add for manually matched track: '%s' (%s:%s)",
|
||||||
|
spotify_track_data.get('name', 'Unknown Track'),
|
||||||
|
track_source,
|
||||||
|
track_id,
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
track_name = spotify_track_data.get('name', 'Unknown Track')
|
track_name = spotify_track_data.get('name', 'Unknown Track')
|
||||||
artists = spotify_track_data.get('artists', [])
|
artists = spotify_track_data.get('artists', [])
|
||||||
if artists:
|
if artists:
|
||||||
|
|
|
||||||
56
pr_description.md
Normal file
56
pr_description.md
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Adds a new **Manual Library Match** tool that lets users map a wishlist/sync source track to an existing library track. Once saved, SoulSync can treat that source track as already owned/found instead of repeatedly trying to download it.
|
||||||
|
|
||||||
|
## What Changed
|
||||||
|
|
||||||
|
- Added a centralized Manual Library Match tool on the Tools page and a shortcut from the Sync page.
|
||||||
|
- Added side-by-side source-track and library-track search UI, plus an existing matches table with removal support.
|
||||||
|
- Added `manual_library_track_matches` persistence with profile/source/source-track scoping.
|
||||||
|
- Added backend search/list/save/delete endpoints for manual matches.
|
||||||
|
- Integrated manual matches into download analysis so matched source tracks are marked found and skipped.
|
||||||
|
- Preserved the distinction between wishlist internal force mode and explicit user Force Download All:
|
||||||
|
- Wishlist/internal `force_download_all` still honors manual matches.
|
||||||
|
- User-facing Force Download All ignores manual matches and downloads anyway.
|
||||||
|
- Updated wishlist cleanup so manual matches are removed from the wishlist through:
|
||||||
|
- manual wishlist cleanup,
|
||||||
|
- automatic cleanup after DB update,
|
||||||
|
- wishlist download analysis.
|
||||||
|
- Prevented manually matched source tracks from being re-added to the wishlist in the common database add path.
|
||||||
|
- Polished the Tools page card so Manual Library Match visually matches the Discovery Pool tool card.
|
||||||
|
|
||||||
|
## Behavior
|
||||||
|
|
||||||
|
- Manual match saved:
|
||||||
|
- `source + source_track_id + profile_id -> library_track_id`
|
||||||
|
- Wishlist cleanup:
|
||||||
|
- removes the item if a manual match exists.
|
||||||
|
- Wishlist add:
|
||||||
|
- skips inserting the item if a manual match already exists, returning a harmless success/no-op.
|
||||||
|
- Wishlist download:
|
||||||
|
- checks manual match first, marks found, skips download, and attempts wishlist removal.
|
||||||
|
- Normal download with Force Download All off:
|
||||||
|
- checks manual match before normal library matching.
|
||||||
|
- Normal download with Force Download All on:
|
||||||
|
- skips manual matches and downloads selected tracks intentionally.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Verified by user:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./.venv/bin/python -m pytest tests/test_manual_library_match.py tests/downloads/test_downloads_master.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Result:
|
||||||
|
|
||||||
|
```text
|
||||||
|
43 passed in 10.29s
|
||||||
|
```
|
||||||
|
|
||||||
|
Additional local verification:
|
||||||
|
|
||||||
|
```text
|
||||||
|
py_compile passed for touched Python files
|
||||||
|
node --check passed for touched JS
|
||||||
|
```
|
||||||
|
|
@ -79,6 +79,32 @@ def test_get_returns_none_when_absent(db):
|
||||||
assert db.get_manual_library_match(1, "spotify", "nonexistent") is None
|
assert db.get_manual_library_match(1, "spotify", "nonexistent") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_add_to_wishlist_skips_manual_matched_track(db):
|
||||||
|
db.save_manual_library_match(1, "spotify", "track-abc", 42)
|
||||||
|
|
||||||
|
ok = db.add_to_wishlist(
|
||||||
|
track_data={
|
||||||
|
"id": "track-abc",
|
||||||
|
"name": "HUMBLE.",
|
||||||
|
"artists": [{"name": "Kendrick Lamar"}],
|
||||||
|
"album": {"name": "DAMN."},
|
||||||
|
"provider": "spotify",
|
||||||
|
},
|
||||||
|
failure_reason="Download failed",
|
||||||
|
profile_id=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert ok is True
|
||||||
|
assert db.get_wishlist_tracks(profile_id=1) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_match_returns_none_when_db_lacks_manual_match_method():
|
||||||
|
class _MinimalDB:
|
||||||
|
pass
|
||||||
|
|
||||||
|
assert mlm.get_match(_MinimalDB(), 1, "spotify", "track-abc") is None
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Service-layer tests
|
# Service-layer tests
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue