diff --git a/docs/plans/2026-07-30-live-remix-filtering.md b/docs/plans/2026-07-30-live-remix-filtering.md index 5a2babf..4e4d38e 100644 --- a/docs/plans/2026-07-30-live-remix-filtering.md +++ b/docs/plans/2026-07-30-live-remix-filtering.md @@ -55,11 +55,11 @@ - [x] run project tests - must pass before next task ### Task 3: Update Web UI and Handlers -- [ ] Update `ArtistData` or similar view models to include the new filter booleans -- [ ] Update `internal/web/handlers.go` to handle the new toggle POST requests -- [ ] Update `internal/web/templates/artist.html` to show new toggles for Live and Remix -- [ ] write tests for new web handlers -- [ ] run project tests - must pass before next task +- [x] Update `ArtistData` or similar view models to include the new filter booleans +- [x] Update `internal/web/handlers.go` to handle the new toggle POST requests +- [x] Update `internal/web/templates/artist.html` to show new toggles for Live and Remix +- [x] write tests for new web handlers +- [x] run project tests - must pass before next task ### Task 4: Verify and Document - [ ] Verify the scanner correctly suppresses "Live" and "Remix" types when toggles are enabled diff --git a/internal/web/handlers.go b/internal/web/handlers.go index 0c2ae44..41a90e1 100644 --- a/internal/web/handlers.go +++ b/internal/web/handlers.go @@ -82,6 +82,9 @@ type ArtistData struct { Name string MBID string IgnoreSingles bool + IgnoreCompilations bool + IgnoreLive bool + IgnoreRemix bool LocalAlbums []LocalAlbumView Missing []MissingReleaseView UIBaseURL string @@ -146,11 +149,14 @@ func (s *Server) buildArtistData(ctx context.Context, id string) (*ArtistData, e } data := &ArtistData{ - ID: settings.ID, - Name: settings.Name, - MBID: settings.MBID, - IgnoreSingles: settings.IgnoreSingles, - UIBaseURL: s.uiBaseURL, + ID: settings.ID, + Name: settings.Name, + MBID: settings.MBID, + IgnoreSingles: settings.IgnoreSingles, + IgnoreCompilations: settings.IgnoreCompilations, + IgnoreLive: settings.IgnoreLive, + IgnoreRemix: settings.IgnoreRemix, + UIBaseURL: s.uiBaseURL, } for _, a := range locals { data.LocalAlbums = append(data.LocalAlbums, LocalAlbumView{Title: a.Title}) @@ -318,3 +324,99 @@ func (s *Server) toggleIgnoreSingles(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/artist/"+id, http.StatusSeeOther) } + +// toggleIgnoreCompilations handles POST /artist/{id}/ignore-compilations which flips the +// artist's ignore_compilations flag. +func (s *Server) toggleIgnoreCompilations(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + if !s.sameOrigin(r) { + http.Error(w, "forbidden: cross-origin request", http.StatusForbidden) + return + } + id := r.PathValue("id") + if id == "" || !isValidID(id) { + http.NotFound(w, r) + return + } + + settings, err := database.GetArtistSettings(s.db, id) + if err != nil { + http.Error(w, fmt.Sprintf("load artist: %v", err), http.StatusInternalServerError) + return + } + if err := database.UpdateArtistSettings(s.db, id, map[string]interface{}{ + "ignore_compilations": !settings.IgnoreCompilations, + }); err != nil { + http.Error(w, fmt.Sprintf("update artist: %v", err), http.StatusInternalServerError) + return + } + + http.Redirect(w, r, "/artist/"+id, http.StatusSeeOther) +} + +// toggleIgnoreLive handles POST /artist/{id}/ignore-live which flips the +// artist's ignore_live flag. +func (s *Server) toggleIgnoreLive(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + if !s.sameOrigin(r) { + http.Error(w, "forbidden: cross-origin request", http.StatusForbidden) + return + } + id := r.PathValue("id") + if id == "" || !isValidID(id) { + http.NotFound(w, r) + return + } + + settings, err := database.GetArtistSettings(s.db, id) + if err != nil { + http.Error(w, fmt.Sprintf("load artist: %v", err), http.StatusInternalServerError) + return + } + if err := database.UpdateArtistSettings(s.db, id, map[string]interface{}{ + "ignore_live": !settings.IgnoreLive, + }); err != nil { + http.Error(w, fmt.Sprintf("update artist: %v", err), http.StatusInternalServerError) + return + } + + http.Redirect(w, r, "/artist/"+id, http.StatusSeeOther) +} + +// toggleIgnoreRemix handles POST /artist/{id}/ignore-remix which flips the +// artist's ignore_remix flag. +func (s *Server) toggleIgnoreRemix(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + if !s.sameOrigin(r) { + http.Error(w, "forbidden: cross-origin request", http.StatusForbidden) + return + } + id := r.PathValue("id") + if id == "" || !isValidID(id) { + http.NotFound(w, r) + return + } + + settings, err := database.GetArtistSettings(s.db, id) + if err != nil { + http.Error(w, fmt.Sprintf("load artist: %v", err), http.StatusInternalServerError) + return + } + if err := database.UpdateArtistSettings(s.db, id, map[string]interface{}{ + "ignore_remix": !settings.IgnoreRemix, + }); err != nil { + http.Error(w, fmt.Sprintf("update artist: %v", err), http.StatusInternalServerError) + return + } + + http.Redirect(w, r, "/artist/"+id, http.StatusSeeOther) +} diff --git a/internal/web/server.go b/internal/web/server.go index 6c2a1e8..fc78ccc 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -58,6 +58,9 @@ func NewServer(cfg *config.ServerConfig, db *database.DB, uiBaseURL string, thre mux.HandleFunc("/artist/{id}/ignore", s.ignoreOrRestore) mux.HandleFunc("/artist/{id}/restore", s.ignoreOrRestore) mux.HandleFunc("/artist/{id}/ignore-singles", s.toggleIgnoreSingles) + mux.HandleFunc("/artist/{id}/ignore-compilations", s.toggleIgnoreCompilations) + mux.HandleFunc("/artist/{id}/ignore-live", s.toggleIgnoreLive) + mux.HandleFunc("/artist/{id}/ignore-remix", s.toggleIgnoreRemix) mux.HandleFunc("/archive", s.handleArchive) s.mux = mux return s @@ -171,11 +174,11 @@ func (s *Server) sameOrigin(r *http.Request) bool { // ArtistSummary is the dashboard projection of a single monitored artist and // its missing-release count. type ArtistSummary struct { - ID string - Name string - MBID string - MissingCount int - Monitored bool + ID string + Name string + MBID string + MissingCount int + Monitored bool } // DashboardData is the view model passed to the dashboard template. diff --git a/internal/web/templates/artist.html b/internal/web/templates/artist.html index 83b7199..c24859a 100644 --- a/internal/web/templates/artist.html +++ b/internal/web/templates/artist.html @@ -30,6 +30,21 @@ {{ if .IgnoreSingles }} (singles currently ignored){{ end }} +
+ + {{ if .IgnoreCompilations }} (compilations currently ignored){{ end }} +
+ +
+ + {{ if .IgnoreLive }} (live recordings currently ignored){{ end }} +
+ +
+ + {{ if .IgnoreRemix }} (remixes currently ignored){{ end }} +
+

Local albums (Subsonic)

{{ if .LocalAlbums }}