LB Sync tab: fix track counts + auto-mirror on discovery complete
Two follow-ups to the LB Sync tab work: 1. **Track counts all showed 0.** The ``/api/discover/listenbrainz/*`` endpoints assemble a JSPF-shaped payload but drop the cached ``track_count`` field from the underlying ``listenbrainz_playlists`` row — the JSON the frontend sees only carries ``title`` / ``creator`` / ``annotation`` / an empty ``track`` array. The Discover-page renderer worked around it by hard-coding a fallback of 50; the Sync-page renderer had no such fallback, so every card displayed "0 tracks". Backend now includes ``track_count`` directly in each playlist payload (it's already in the cached row) so any frontend can render an accurate count without resorting to a default. JS still falls back to ``annotation.track_count`` and then ``track.length`` for older callers. 2. **LB playlists never landed in Mirrored Playlists.** The existing ``/api/listenbrainz/sync/start/<mbid>`` endpoint runs the converted Spotify tracks through ``_run_sync_task`` — i.e. it pushes them to the user's media server (Plex / Jellyfin / Navidrome / SoulSync) as a server-side playlist. It does NOT call ``database.mirror_playlist``. So no ``mirrored_playlists`` row gets created and the playlist can't be picked up by the Auto-Sync scheduler, can't show up under the Mirrored tab, doesn't participate in pipeline automations — the whole point of the Sync-tab unification. Tidal works because Tidal mirrors on tab load with raw tracks then enriches via discovery. LB tracks only have provider IDs *after* discovery, so the equivalent moment for LB is "discovery complete". Added ``_mirrorListenBrainzAfterDiscovery(mbid)`` that pulls the matched ``spotify_data`` out of ``discovery_results`` and posts to ``/api/mirror-playlist`` via the existing ``mirrorPlaylist`` helper. Hooked into both the WebSocket and HTTP-poll completion handlers of ``startListenBrainzDiscoveryPolling``. UPSERT-keyed on (source, source_playlist_id, profile_id), so re-running discovery is a safe no-op refresh. Result: any LB playlist the user discovers (from either the Discover page or the new Sync tab) now lands in ``mirrored_playlists`` with ``source='listenbrainz'`` + matched tracks carrying canonical ``extra_data`` JSON, ready for the Auto-Sync refresh + sync pipeline wired up in Phase 1a + 1b.
This commit is contained in:
parent
969d5ffc1b
commit
f521be7720
3 changed files with 77 additions and 1 deletions
|
|
@ -29986,6 +29986,7 @@ def _get_lb_discover_playlists(playlist_type):
|
|||
"identifier": f"https://listenbrainz.org/playlist/{playlist['playlist_mbid']}",
|
||||
"title": playlist['title'],
|
||||
"creator": playlist['creator'],
|
||||
"track_count": playlist.get('track_count', 0),
|
||||
"annotation": playlist.get('annotation', {}),
|
||||
"track": []
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,9 @@ function renderListenBrainzSyncPlaylists() {
|
|||
const title = inner.title || inner.name || 'ListenBrainz Playlist';
|
||||
const creator = inner.creator || 'ListenBrainz';
|
||||
let count = 0;
|
||||
if (inner.annotation && inner.annotation.track_count) {
|
||||
if (inner.track_count) {
|
||||
count = inner.track_count;
|
||||
} else if (inner.annotation && inner.annotation.track_count) {
|
||||
count = inner.annotation.track_count;
|
||||
} else if (Array.isArray(inner.track) && inner.track.length > 0) {
|
||||
count = inner.track.length;
|
||||
|
|
|
|||
|
|
@ -10793,6 +10793,72 @@ async function resetBeatportChart(urlHash) {
|
|||
// LISTENBRAINZ PLAYLIST DISCOVERY & SYNC
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Auto-mirror a ListenBrainz playlist into the mirrored_playlists
|
||||
* table after discovery completes. Pattern parity with Tidal —
|
||||
* Tidal mirrors on tab load with raw tracks, then discovery enriches;
|
||||
* LB tracks only have provider IDs after discovery, so we mirror at
|
||||
* the end. Idempotent (UPSERT on source + source_playlist_id +
|
||||
* profile_id), so calling it twice is a no-op.
|
||||
*/
|
||||
function _mirrorListenBrainzAfterDiscovery(playlistMbid) {
|
||||
try {
|
||||
const state = listenbrainzPlaylistStates[playlistMbid];
|
||||
if (!state || !state.playlist) return;
|
||||
if (typeof mirrorPlaylist !== 'function') return;
|
||||
|
||||
const results = state.discovery_results || [];
|
||||
if (!results.length) return;
|
||||
|
||||
const tracks = results
|
||||
.filter(r => r && r.spotify_data && r.spotify_data.id)
|
||||
.map(r => {
|
||||
const sp = r.spotify_data;
|
||||
const artistName = Array.isArray(sp.artists)
|
||||
? (typeof sp.artists[0] === 'object' ? sp.artists[0].name : sp.artists[0])
|
||||
: (sp.artists || '');
|
||||
const albumName = (sp.album && typeof sp.album === 'object')
|
||||
? sp.album.name : (sp.album || '');
|
||||
const albumImage = (sp.album && sp.album.images && sp.album.images[0])
|
||||
? sp.album.images[0].url : (sp.image_url || null);
|
||||
return {
|
||||
track_name: sp.name || '',
|
||||
artist_name: artistName || '',
|
||||
album_name: albumName || '',
|
||||
duration_ms: sp.duration_ms || 0,
|
||||
image_url: albumImage,
|
||||
source_track_id: sp.id || '',
|
||||
extra_data: JSON.stringify({
|
||||
discovered: true,
|
||||
provider: sp.source || 'spotify',
|
||||
confidence: r.confidence || 1.0,
|
||||
matched_data: sp,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
if (!tracks.length) {
|
||||
console.warn(`🪞 [LB Mirror] No matched tracks in '${state.playlist.name}', skipping mirror`);
|
||||
return;
|
||||
}
|
||||
|
||||
mirrorPlaylist(
|
||||
'listenbrainz',
|
||||
playlistMbid,
|
||||
state.playlist.name || 'ListenBrainz Playlist',
|
||||
tracks,
|
||||
{
|
||||
owner: state.playlist.creator || 'ListenBrainz',
|
||||
description: state.playlist.description || '',
|
||||
image_url: state.playlist.image_url || '',
|
||||
}
|
||||
);
|
||||
console.log(`🪞 [LB Mirror] Mirrored '${state.playlist.name}' with ${tracks.length} matched tracks`);
|
||||
} catch (err) {
|
||||
console.warn('LB mirror-after-discovery failed:', err);
|
||||
}
|
||||
}
|
||||
|
||||
function startListenBrainzDiscoveryPolling(playlistMbid) {
|
||||
console.log(`🔄 Starting ListenBrainz discovery polling for: ${playlistMbid}`);
|
||||
|
||||
|
|
@ -10843,6 +10909,10 @@ function startListenBrainzDiscoveryPolling(playlistMbid) {
|
|||
const playlistIdEl = `discover-lb-playlist-${playlistMbid}`;
|
||||
const syncBtn = document.getElementById(`${playlistIdEl}-sync-btn`);
|
||||
if (syncBtn) syncBtn.style.display = 'inline-block';
|
||||
// Mirror matched tracks → mirrored_playlists table so the
|
||||
// playlist participates in Auto-Sync schedules just like
|
||||
// Tidal / Qobuz / Spotify mirrors do.
|
||||
_mirrorListenBrainzAfterDiscovery(playlistMbid);
|
||||
showToast('ListenBrainz discovery complete!', 'success');
|
||||
}
|
||||
};
|
||||
|
|
@ -10935,6 +11005,9 @@ function startListenBrainzDiscoveryPolling(playlistMbid) {
|
|||
}
|
||||
|
||||
console.log('✅ ListenBrainz discovery complete:', playlistMbid);
|
||||
// Mirror matched tracks → mirrored_playlists table so
|
||||
// the playlist participates in Auto-Sync schedules.
|
||||
_mirrorListenBrainzAfterDiscovery(playlistMbid);
|
||||
showToast('ListenBrainz discovery complete!', 'success');
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue