display all listenbrainz playlsts in unique categories.
This commit is contained in:
parent
681d4b3cd2
commit
6750c20dc4
2 changed files with 155 additions and 19 deletions
|
|
@ -285,21 +285,21 @@ class ListenBrainzManager:
|
|||
logger.info(f"✅ Fetched {covers_found}/{len(track_data_list)} cover art URLs")
|
||||
|
||||
def _cleanup_old_playlists(self):
|
||||
"""Remove old playlists, keeping only the 4 most recent per type"""
|
||||
"""Remove old playlists, keeping only the 25 most recent per type"""
|
||||
conn = self._get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
|
||||
# For each playlist type, keep only the 4 most recent
|
||||
# For each playlist type, keep only the 25 most recent
|
||||
playlist_types = ['created_for', 'user', 'collaborative']
|
||||
|
||||
for playlist_type in playlist_types:
|
||||
try:
|
||||
# Get IDs of playlists to delete (all except 4 most recent)
|
||||
# Get IDs of playlists to delete (all except 25 most recent)
|
||||
cursor.execute("""
|
||||
SELECT id FROM listenbrainz_playlists
|
||||
WHERE playlist_type = ?
|
||||
ORDER BY last_updated DESC
|
||||
LIMIT -1 OFFSET 4
|
||||
LIMIT -1 OFFSET 25
|
||||
""", (playlist_type,))
|
||||
|
||||
old_playlist_ids = [row[0] for row in cursor.fetchall()]
|
||||
|
|
@ -330,7 +330,7 @@ class ListenBrainzManager:
|
|||
return count > 0
|
||||
|
||||
def get_cached_playlists(self, playlist_type: str) -> List[Dict]:
|
||||
"""Get cached playlists of a specific type from database (limited to 4 most recent)"""
|
||||
"""Get cached playlists of a specific type from database (up to 25 most recent)"""
|
||||
conn = self._get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
|
@ -339,7 +339,7 @@ class ListenBrainzManager:
|
|||
FROM listenbrainz_playlists
|
||||
WHERE playlist_type = ?
|
||||
ORDER BY last_updated DESC
|
||||
LIMIT 4
|
||||
LIMIT 25
|
||||
""", (playlist_type,))
|
||||
|
||||
playlists = []
|
||||
|
|
|
|||
|
|
@ -31502,6 +31502,7 @@ async function openDownloadModalForGenre(genreName) {
|
|||
let listenbrainzPlaylistsCache = {}; // Store playlists by type
|
||||
let listenbrainzTracksCache = {}; // Store tracks for each playlist
|
||||
let activeListenBrainzTab = 'recommendations'; // Track active tab
|
||||
let activeListenBrainzSubTab = null; // Track active sub-tab within recommendations
|
||||
|
||||
async function initializeListenBrainzTabs() {
|
||||
try {
|
||||
|
|
@ -31622,17 +31623,47 @@ function switchListenBrainzTab(tabId) {
|
|||
loadListenBrainzTabContent(tabId);
|
||||
}
|
||||
|
||||
async function loadListenBrainzTabContent(tabId) {
|
||||
const container = document.getElementById('listenbrainz-tab-content');
|
||||
if (!container) return;
|
||||
function groupListenBrainzPlaylists(playlists) {
|
||||
const groups = {};
|
||||
const groupOrder = [];
|
||||
|
||||
const playlists = listenbrainzPlaylistsCache[tabId] || [];
|
||||
if (playlists.length === 0) {
|
||||
container.innerHTML = '<div class="discover-empty"><p>No playlists in this category</p></div>';
|
||||
return;
|
||||
playlists.forEach(playlist => {
|
||||
const playlistData = playlist.playlist || playlist;
|
||||
const title = (playlistData.title || '').toLowerCase();
|
||||
|
||||
let groupName;
|
||||
if (title.includes('weekly jams')) {
|
||||
groupName = 'Weekly Jams';
|
||||
} else if (title.includes('weekly exploration')) {
|
||||
groupName = 'Weekly Exploration';
|
||||
} else if (title.includes('top discoveries')) {
|
||||
groupName = 'Top Discoveries';
|
||||
} else if (title.includes('top missed recordings')) {
|
||||
groupName = 'Top Missed Recordings';
|
||||
} else if (title.includes('daily jams')) {
|
||||
groupName = 'Daily Jams';
|
||||
} else {
|
||||
groupName = 'Other';
|
||||
}
|
||||
|
||||
if (!groups[groupName]) {
|
||||
groups[groupName] = [];
|
||||
groupOrder.push(groupName);
|
||||
}
|
||||
groups[groupName].push(playlist);
|
||||
});
|
||||
|
||||
// Move "Other" to the end if it exists
|
||||
const otherIdx = groupOrder.indexOf('Other');
|
||||
if (otherIdx !== -1 && otherIdx !== groupOrder.length - 1) {
|
||||
groupOrder.splice(otherIdx, 1);
|
||||
groupOrder.push('Other');
|
||||
}
|
||||
|
||||
// Build HTML for all playlists in this tab
|
||||
return { groups, groupOrder };
|
||||
}
|
||||
|
||||
function buildListenBrainzPlaylistsHtml(playlists, tabId) {
|
||||
let html = '';
|
||||
playlists.forEach((playlist, index) => {
|
||||
const playlistData = playlist.playlist || playlist;
|
||||
|
|
@ -31702,18 +31733,123 @@ async function loadListenBrainzTabContent(tabId) {
|
|||
</div>
|
||||
`;
|
||||
});
|
||||
return html;
|
||||
}
|
||||
|
||||
container.innerHTML = html;
|
||||
|
||||
// Load tracks for all playlists in this tab
|
||||
playlists.forEach((playlist, index) => {
|
||||
function loadTracksForPlaylists(playlists) {
|
||||
playlists.forEach((playlist) => {
|
||||
const playlistData = playlist.playlist || playlist;
|
||||
const identifier = playlistData.identifier?.split('/').pop() || '';
|
||||
const playlistId = `discover-lb-playlist-${identifier}`; // Use consistent MBID-based ID
|
||||
const playlistId = `discover-lb-playlist-${identifier}`;
|
||||
loadListenBrainzPlaylistTracks(identifier, playlistId);
|
||||
});
|
||||
}
|
||||
|
||||
function switchListenBrainzSubTab(groupId) {
|
||||
activeListenBrainzSubTab = groupId;
|
||||
|
||||
// Update sub-tab buttons
|
||||
const subTabs = document.querySelectorAll('#lb-subtabs-bar .lb-subtab');
|
||||
subTabs.forEach(tab => {
|
||||
if (tab.dataset.group === groupId) {
|
||||
tab.classList.add('active');
|
||||
} else {
|
||||
tab.classList.remove('active');
|
||||
}
|
||||
});
|
||||
|
||||
// Show/hide sub-tab content panels
|
||||
const panels = document.querySelectorAll('.lb-subtab-panel');
|
||||
panels.forEach(panel => {
|
||||
if (panel.dataset.group === groupId) {
|
||||
panel.style.display = 'block';
|
||||
// Load tracks for playlists in this panel if not already loaded
|
||||
const unloaded = panel.querySelectorAll('.discover-loading');
|
||||
if (unloaded.length > 0) {
|
||||
const groupPlaylists = panel._playlists;
|
||||
if (groupPlaylists) {
|
||||
loadTracksForPlaylists(groupPlaylists);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panel.style.display = 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function loadListenBrainzTabContent(tabId) {
|
||||
const container = document.getElementById('listenbrainz-tab-content');
|
||||
if (!container) return;
|
||||
|
||||
const playlists = listenbrainzPlaylistsCache[tabId] || [];
|
||||
if (playlists.length === 0) {
|
||||
container.innerHTML = '<div class="discover-empty"><p>No playlists in this category</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// For recommendations tab with multiple playlists, group into sub-tabs
|
||||
if (tabId === 'recommendations' && playlists.length > 1) {
|
||||
const { groups, groupOrder } = groupListenBrainzPlaylists(playlists);
|
||||
|
||||
// If only one group, no need for sub-tabs
|
||||
if (groupOrder.length <= 1) {
|
||||
const html = buildListenBrainzPlaylistsHtml(playlists, tabId);
|
||||
container.innerHTML = html;
|
||||
loadTracksForPlaylists(playlists);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build sub-tabs bar
|
||||
const firstGroup = activeListenBrainzSubTab && groupOrder.includes(activeListenBrainzSubTab)
|
||||
? activeListenBrainzSubTab
|
||||
: groupOrder[0];
|
||||
activeListenBrainzSubTab = firstGroup;
|
||||
|
||||
let subTabsHtml = '<div class="decade-tabs-inner" id="lb-subtabs-bar" style="margin-bottom: 16px;">';
|
||||
groupOrder.forEach(groupName => {
|
||||
const isActive = groupName === firstGroup;
|
||||
const count = groups[groupName].length;
|
||||
subTabsHtml += `
|
||||
<button class="decade-tab lb-subtab${isActive ? ' active' : ''}"
|
||||
onclick="switchListenBrainzSubTab('${groupName}')"
|
||||
data-group="${groupName}"
|
||||
style="font-size: 13px; padding: 8px 16px;">
|
||||
${groupName} (${count})
|
||||
</button>
|
||||
`;
|
||||
});
|
||||
subTabsHtml += '</div>';
|
||||
|
||||
// Build content panels for each group
|
||||
let panelsHtml = '';
|
||||
groupOrder.forEach(groupName => {
|
||||
const isActive = groupName === firstGroup;
|
||||
panelsHtml += `<div class="lb-subtab-panel" data-group="${groupName}" style="display: ${isActive ? 'block' : 'none'};">`;
|
||||
panelsHtml += buildListenBrainzPlaylistsHtml(groups[groupName], tabId);
|
||||
panelsHtml += '</div>';
|
||||
});
|
||||
|
||||
container.innerHTML = subTabsHtml + panelsHtml;
|
||||
|
||||
// Store playlist references on panels for lazy loading
|
||||
groupOrder.forEach(groupName => {
|
||||
const panel = container.querySelector(`.lb-subtab-panel[data-group="${groupName}"]`);
|
||||
if (panel) {
|
||||
panel._playlists = groups[groupName];
|
||||
}
|
||||
});
|
||||
|
||||
// Load tracks only for the active sub-tab
|
||||
loadTracksForPlaylists(groups[firstGroup]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Default: flat list for user/collaborative tabs (or single-group recommendations)
|
||||
const html = buildListenBrainzPlaylistsHtml(playlists, tabId);
|
||||
container.innerHTML = html;
|
||||
loadTracksForPlaylists(playlists);
|
||||
}
|
||||
|
||||
async function loadListenBrainzPlaylistTracks(identifier, playlistId) {
|
||||
try {
|
||||
const playlistContainer = document.getElementById(`${playlistId}-playlist`);
|
||||
|
|
|
|||
Loading…
Reference in a new issue