fix: address code review findings

- Fix duplicate Telegram notifications: SyncArtistDiscography no longer wipes
  notifications_sent for the whole artist on every cache-miss re-sync; only
  markers for releases that disappear are pruned (FK-safe via INSERT OR REPLACE
  + rgid NOT IN (...)).
- Cache empty MusicBrainz discographies via a new artist_settings.last_synced
  column (migration 009) so zero-release artists honor the TTL instead of being
  re-fetched every cycle.
- Wire the Web UI server and Telegram notifier scheduler into main.run/NewApp.
- Guard startPeriodicSync against overlapping syncs with a done-channel slot.
- Add server.public_url config; NewServerWithConfig derives reachable links
  and no longer advertises the 0.0.0.0 bind address.
- Web handlers: use scanner.ScanArtist per artist, drop always-false
  releaseIgnored lookup and dead endsWith, thread configured threshold.
- Limit :memory: DB pool to one connection so migrations and queries share the
  same in-memory store.
This commit is contained in:
2026-07-19 23:38:33 +03:00
parent e493a4d228
commit 389d177d85
15 changed files with 386 additions and 126 deletions

View File

@@ -4,19 +4,27 @@ import (
"database/sql"
"errors"
"fmt"
"time"
)
// DBer is the minimal query interface satisfied by both *sql.DB and *sql.Tx,
// so callers can run statements inside or outside a transaction.
type DBer interface {
Exec(query string, args ...interface{}) (sql.Result, error)
}
// GetArtistSettings retrieves an artist_settings row by ID.
// Returns sql.ErrNoRows if the artist is not found.
func GetArtistSettings(db *DB, id string) (*ArtistSettings, error) {
var (
s ArtistSettings
mbid sql.NullString
s ArtistSettings
mbid sql.NullString
lastSynced sql.NullTime
)
err := db.Conn().QueryRow(
"SELECT id, name, mbid, ignore_singles, ignore_compilations, monitored FROM artist_settings WHERE id = ?",
"SELECT id, name, mbid, ignore_singles, ignore_compilations, monitored, last_synced FROM artist_settings WHERE id = ?",
id,
).Scan(&s.ID, &s.Name, &mbid, &s.IgnoreSingles, &s.IgnoreCompilations, &s.Monitored)
).Scan(&s.ID, &s.Name, &mbid, &s.IgnoreSingles, &s.IgnoreCompilations, &s.Monitored, &lastSynced)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrArtistNotFound
@@ -24,9 +32,26 @@ func GetArtistSettings(db *DB, id string) (*ArtistSettings, error) {
return nil, err
}
s.MBID = mbid.String
if lastSynced.Valid {
s.LastSynced = lastSynced.Time
}
return &s, nil
}
// TouchArtistSynced records that the artist was synced at the given time. It
// is used by the MusicBrainz pipeline to mark a successful sync (even one that
// found zero release groups) so the cache TTL is honoured.
func TouchArtistSynced(db DBer, artistID string, syncedAt time.Time) error {
_, err := db.Exec(
"UPDATE artist_settings SET last_synced = ? WHERE id = ?",
FormatCachedAt(syncedAt), artistID,
)
if err != nil {
return fmt.Errorf("touch artist synced: %w", err)
}
return nil
}
// SaveArtistSettings inserts or replaces an artist_settings row.
func SaveArtistSettings(db *DB, settings *ArtistSettings) error {
_, err := db.Conn().Exec(