From e38880b81562bbee72e4f56519f81d10af15a62f Mon Sep 17 00:00:00 2001 From: dammitjeff <44111923+dammitjeff@users.noreply.github.com> Date: Thu, 18 Jun 2026 09:45:31 -0700 Subject: [PATCH 1/2] Fixed bug where playlist cards wouldn't automatically refresh after a run, moved up tracklist pull priority in run --- src/main/main.go | 8 +++++++- src/web/frontend/src/components/Settings.jsx | 14 +++++++++++++- .../frontend/src/components/ui/PlaylistCard.jsx | 7 ++++--- src/web/frontend/src/lib/listenbrainz.js | 5 +++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/main/main.go b/src/main/main.go index 7fe3d0dc..1a91115e 100644 --- a/src/main/main.go +++ b/src/main/main.go @@ -165,6 +165,8 @@ func main() { log.Fatal(srv.Start()) } + slog.Info("Pulling playlist", "playlist", cfg.Flags.Playlist) + var tracks []*models.Track var err error if strings.HasPrefix(cfg.Flags.Playlist, "custom-") { @@ -178,11 +180,15 @@ func main() { tracks, err = disc.Discover() } - if err != nil { + if err != nil { slog.Error(err.Error(), "notify", true) os.Exit(1) } allTracks := append([]*models.Track(nil), tracks...) + if cfg.ServerCfg.WebDataDir != "" { + backend.WritePlaylistCache(cfg.ServerCfg.WebDataDir, cfg.Flags.Playlist, allTracks, nil) + slog.Info("Saved playlist", "playlist", cfg.Flags.Playlist, "tracks", len(allTracks)) + } client, err := client.NewClient(&cfg) if err != nil { diff --git a/src/web/frontend/src/components/Settings.jsx b/src/web/frontend/src/components/Settings.jsx index 172b8059..ec4fb7e1 100644 --- a/src/web/frontend/src/components/Settings.jsx +++ b/src/web/frontend/src/components/Settings.jsx @@ -19,7 +19,7 @@ import { fetchPathTemplatePresets, addPathTemplatePreset, deletePathTemplatePreset, } from '../lib/api' import { parseSlogLine, cronToFields, highlightEnv } from '../lib/utils' -import { fetchPlaylistTracks } from '../lib/listenbrainz' +import { fetchPlaylistTracks, clearPlaylistCache } from '../lib/listenbrainz' import { motion, AnimatePresence } from 'motion/react' import { Toggle } from './ui/Toggle' import { Button, SectionLabel, Panel, LogRow } from './ui/common' @@ -134,6 +134,7 @@ function CustomPlaylistsSection({ onDelete, showImportModal, setShowImportModal, + refreshTick = 0, }) { return (
@@ -170,6 +171,7 @@ function CustomPlaylistsSection({ onTracklistToggle={() => setOpenTracklist(v => v === cp.id ? null : cp.id)} sourceUrl={cp.source_url || undefined} onDelete={(opts) => onDelete(cp.id, opts)} + refreshTick={refreshTick} /> ) })} @@ -181,6 +183,7 @@ function CustomPlaylistsSection({ playlist={openTracklist} lbUser={null} onRun={() => onSync(openTracklist)} + refreshTick={refreshTick} /> @@ -217,6 +220,8 @@ function HomeSection() { const [rawLog, setRawLog] = useState(false) const logRef = useRef(null) + const [refreshTick, setRefreshTick] = useState(0) + useEffect(() => { Promise.all([ fetchConfig(), @@ -255,6 +260,10 @@ function HomeSection() { const onDone = useCallback(code => { setStatus(code === 0 ? 'done ✓' : code === null ? 'error' : `failed (exit ${code})`) setRunning(false) + if (code !== null) { + clearPlaylistCache() + setRefreshTick(t => t + 1) + } }, []) const { connect, disconnect } = useSSE({ onLine, onDone }) @@ -362,6 +371,7 @@ function HomeSection() { nextRunText={nextRunText(p.value)} tracklistOpen={openTracklist === p.value} onTracklistToggle={() => setOpenTracklist(v => v === p.value ? null : p.value)} + refreshTick={refreshTick} /> ))}
@@ -369,6 +379,7 @@ function HomeSection() { { await startRun(openTracklist, 'normal', true, false) setRunning(true) @@ -384,6 +395,7 @@ function HomeSection() { {/* Custom Playlists */} { if (!playlist) return return loadTracks(false) - }, [playlist]) + }, [playlist, refreshTick]) const handleFetch = () => { if (!lbUser) return @@ -376,6 +376,7 @@ export function PlaylistCard({ trackId, artworkUrl, sourceUrl, + refreshTick = 0, }) { const { value, name } = playlist // trackFetchId: use real playlist ID (custom playlists) if provided, else fall back to value @@ -428,7 +429,7 @@ export function PlaylistCard({ cancelled = true if (retryTimer) clearTimeout(retryTimer) } - }, [trackFetchId, s.enabled]) + }, [trackFetchId, s.enabled, refreshTick]) useEffect(() => { if (bgCovers.length < 2) return diff --git a/src/web/frontend/src/lib/listenbrainz.js b/src/web/frontend/src/lib/listenbrainz.js index 583f2e71..0ea3b2ff 100644 --- a/src/web/frontend/src/lib/listenbrainz.js +++ b/src/web/frontend/src/lib/listenbrainz.js @@ -1,6 +1,11 @@ // Session-level cache — avoids repeat fetches on open/close within the same page load. const memCache = new Map() +export function clearPlaylistCache(playlistType) { + if (playlistType) memCache.delete(playlistType) + else memCache.clear() +} + export async function fetchPlaylistTracks(playlistType, options = {}) { const key = playlistType if (!options.force && memCache.has(key)) return memCache.get(key) From d1c0aa08d1c6a23999f6abe8c7c67abac1b80d83 Mon Sep 17 00:00:00 2001 From: dammitjeff <44111923+dammitjeff@users.noreply.github.com> Date: Thu, 18 Jun 2026 09:56:35 -0700 Subject: [PATCH 2/2] dead code cleanup --- src/main/main.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/main.go b/src/main/main.go index 1a91115e..2c818342 100644 --- a/src/main/main.go +++ b/src/main/main.go @@ -223,14 +223,6 @@ func main() { } } - if cfg.ServerCfg.Enabled { - added := make(map[string]bool) - for _, t := range tracks { - added[t.CleanTitle+"|"+t.Artist] = true - } - backend.WritePlaylistCache(cfg.Flags.CfgPath, cfg.Flags.Playlist, allTracks, added) - } - if err := client.CreatePlaylist(tracks); err != nil { slog.Warn(err.Error()) } else {