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 {