Propagate playlist provenance when adding to wishlist from download modal.
Manual "Add to Wishlist" from playlist modals now stores playlist_id, playlist_name, and organize_by_playlist in source_context so wishlist requeue can resolve playlist-folder layout instead of defaulting to album. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
83e3f6b660
commit
269e89d514
2 changed files with 127 additions and 8 deletions
|
|
@ -435,6 +435,41 @@ def test_get_wishlist_cycle_returns_stored_value():
|
|||
assert payload == {"cycle": "singles"}
|
||||
|
||||
|
||||
def test_add_album_track_to_wishlist_preserves_playlist_modal_context():
|
||||
runtime, service, _db, _logger, _activity_calls = _build_runtime()
|
||||
track = {
|
||||
"id": "track-1",
|
||||
"name": "Song One",
|
||||
"artists": [{"name": "Artist One"}],
|
||||
"duration_ms": 1234,
|
||||
}
|
||||
artist = {"id": "artist-1", "name": "Artist One"}
|
||||
album = {"id": "album-1", "name": "Album One"}
|
||||
|
||||
payload, status = add_album_track_to_wishlist(
|
||||
runtime,
|
||||
track=track,
|
||||
artist=artist,
|
||||
album=album,
|
||||
source_type="playlist",
|
||||
source_context={
|
||||
"playlist_id": "37i9dQZF1DX0XUsuxWHRQd",
|
||||
"playlist_name": "Daily Mix",
|
||||
"organize_by_playlist": True,
|
||||
"added_from": "download_modal",
|
||||
},
|
||||
)
|
||||
|
||||
assert status == 200
|
||||
assert payload["success"] is True
|
||||
add_call = service.add_calls[0]
|
||||
assert add_call["source_type"] == "playlist"
|
||||
assert add_call["source_context"]["playlist_id"] == "37i9dQZF1DX0XUsuxWHRQd"
|
||||
assert add_call["source_context"]["playlist_name"] == "Daily Mix"
|
||||
assert add_call["source_context"]["organize_by_playlist"] is True
|
||||
assert add_call["source_context"]["added_from"] == "download_modal"
|
||||
|
||||
|
||||
def test_add_album_track_to_wishlist_builds_spotify_payload_and_merges_context():
|
||||
runtime, service, _db, _logger, _activity_calls = _build_runtime()
|
||||
track = {
|
||||
|
|
|
|||
|
|
@ -1315,6 +1315,82 @@ async function handleWishlistDownloadNow() {
|
|||
registerArtistDownload(artist, album, virtualPlaylistId, albumType);
|
||||
}
|
||||
|
||||
/** Playlist-modal ids that should NOT receive playlist-folder wishlist provenance. */
|
||||
const MODAL_ALBUM_WISHLIST_PREFIXES = [
|
||||
'artist_album_', 'discover_album_', 'enhanced_search_album_', 'seasonal_album_',
|
||||
'spotify_library_', 'beatport_release_', 'discover_cache_',
|
||||
];
|
||||
const MODAL_SINGLE_TRACK_WISHLIST_PREFIXES = [
|
||||
'enhanced_search_track_', 'gsearch_track_',
|
||||
];
|
||||
const MODAL_NON_PLAYLIST_WISHLIST_PREFIXES = [
|
||||
'issue_download_', 'library_redownload_', 'redownload_',
|
||||
];
|
||||
|
||||
function isModalPlaylistWishlistContext(playlistId) {
|
||||
const id = String(playlistId || '');
|
||||
if (!id || id === 'wishlist' || id.startsWith('wishlist_')) {
|
||||
return false;
|
||||
}
|
||||
if (MODAL_ALBUM_WISHLIST_PREFIXES.some((p) => id.startsWith(p))) {
|
||||
return false;
|
||||
}
|
||||
if (MODAL_SINGLE_TRACK_WISHLIST_PREFIXES.some((p) => id.startsWith(p))) {
|
||||
return false;
|
||||
}
|
||||
if (MODAL_NON_PLAYLIST_WISHLIST_PREFIXES.some((p) => id.startsWith(p))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function resolveModalWishlistSourceType(playlistId) {
|
||||
return isModalPlaylistWishlistContext(playlistId) ? 'playlist' : 'album';
|
||||
}
|
||||
|
||||
/**
|
||||
* Build source_context for wishlist rows added from a download modal.
|
||||
* Playlist modals include playlist_id/name so wishlist requeue can use playlist-folder layout.
|
||||
*/
|
||||
function buildModalWishlistSourceContext(playlistId, process, trackAlbum, trackArtist, trackAlbumType) {
|
||||
const timestamp = new Date().toISOString();
|
||||
if (!isModalPlaylistWishlistContext(playlistId)) {
|
||||
return {
|
||||
album_name: trackAlbum?.name,
|
||||
artist_name: trackArtist?.name,
|
||||
album_type: trackAlbumType || 'album',
|
||||
added_from: 'download_modal',
|
||||
timestamp,
|
||||
};
|
||||
}
|
||||
|
||||
const playlistName = process.playlist?.name || process.playlistName || 'Unknown Playlist';
|
||||
const organizeSource = typeof playlistOrganizeSourceForRef === 'function'
|
||||
? playlistOrganizeSourceForRef(playlistId)
|
||||
: 'spotify';
|
||||
const resolveRef = typeof normalizePlaylistOrganizeRef === 'function'
|
||||
? normalizePlaylistOrganizeRef(playlistId, organizeSource)
|
||||
: playlistId;
|
||||
const organizeEnabled = typeof isPlaylistOrganizeEnabled === 'function'
|
||||
? isPlaylistOrganizeEnabled(playlistId)
|
||||
: false;
|
||||
|
||||
const context = {
|
||||
playlist_name: playlistName,
|
||||
playlist_id: resolveRef,
|
||||
playlist_source: organizeSource,
|
||||
added_from: 'download_modal',
|
||||
timestamp,
|
||||
};
|
||||
if (organizeEnabled) {
|
||||
context.organize_by_playlist = true;
|
||||
}
|
||||
if (resolveRef !== playlistId) {
|
||||
context.ui_playlist_ref = playlistId;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all tracks from any download modal to the wishlist
|
||||
* Universal handler for all modal types (artist albums, playlists, YouTube, Tidal, etc.)
|
||||
|
|
@ -1351,8 +1427,12 @@ async function addModalTracksToWishlist(playlistId) {
|
|||
// not for playlists, so we must NOT use it as a blanket default.
|
||||
const processArtist = process.artist || null;
|
||||
const album = process.album || process.playlist || { name: 'Playlist', id: playlistId };
|
||||
const wishlistSourceType = resolveModalWishlistSourceType(playlistId);
|
||||
|
||||
console.log(`🔄 Adding ${tracks.length} tracks from "${album.name}" to wishlist (process artist: ${processArtist?.name || 'per-track'})`);
|
||||
console.log(
|
||||
`🔄 Adding ${tracks.length} tracks from "${album.name}" to wishlist `
|
||||
+ `(source: ${wishlistSourceType}, process artist: ${processArtist?.name || 'per-track'})`
|
||||
);
|
||||
|
||||
// Disable the button to prevent double-clicks
|
||||
const wishlistBtn = document.getElementById(`add-to-wishlist-btn-${playlistId}`);
|
||||
|
|
@ -1394,7 +1474,7 @@ async function addModalTracksToWishlist(playlistId) {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
formattedArtists = [{ name: artist.name }];
|
||||
formattedArtists = [{ name: 'Unknown Artist' }];
|
||||
}
|
||||
|
||||
const formattedTrack = {
|
||||
|
|
@ -1447,6 +1527,14 @@ async function addModalTracksToWishlist(playlistId) {
|
|||
trackArtist = { name: 'Unknown Artist', id: null };
|
||||
}
|
||||
|
||||
const sourceContext = buildModalWishlistSourceContext(
|
||||
playlistId,
|
||||
process,
|
||||
trackAlbum,
|
||||
trackArtist,
|
||||
trackAlbumType,
|
||||
);
|
||||
|
||||
const response = await fetch('/api/add-album-to-wishlist', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
|
@ -1456,12 +1544,8 @@ async function addModalTracksToWishlist(playlistId) {
|
|||
track: formattedTrack,
|
||||
artist: trackArtist,
|
||||
album: trackAlbum,
|
||||
source_type: 'album',
|
||||
source_context: {
|
||||
album_name: trackAlbum.name,
|
||||
artist_name: trackArtist.name,
|
||||
album_type: trackAlbumType
|
||||
}
|
||||
source_type: wishlistSourceType,
|
||||
source_context: sourceContext,
|
||||
})
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue