Flag slave-only songs whose album or artist the master has
Scans now mark slave-only songs as missing from the master when the master has their album folder or at least their artist folder. A new Missing from master filter in Review lists them, split by album and artist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
206b2ab5db
commit
4bae6742a4
9 files changed
+281
-15
No files matched your search
+34
-10
@@ -286,20 +286,21 @@ type Match struct {
|
||||
SlaveProbe json.RawMessage `json:"slave_probe,omitempty"`
|
||||
Verdict json.RawMessage `json:"verdict,omitempty"`
|
||||
Note string `json:"note"`
|
||||
Reason string `json:"reason"` // why an approximate match was chosen
|
||||
Reason string `json:"reason"` // why an approximate match was chosen, or what the master has of a missing song
|
||||
Missing string `json:"missing"` // slave-only song: the master has its "album" or only its "artist"
|
||||
LinkTarget string `json:"link_target"` // where linking puts the slave file (filled by the API)
|
||||
Ignored bool `json:"ignored"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
|
||||
const matchCols = `id, pair_id, rel_slave, rel_master, kind, status, master_ino, master_size, master_mtime,
|
||||
slave_ino, slave_size, slave_mtime, master_probe, slave_probe, verdict, note, reason, ignored, updated_at`
|
||||
slave_ino, slave_size, slave_mtime, master_probe, slave_probe, verdict, note, reason, missing, ignored, updated_at`
|
||||
|
||||
func scanMatch(r scanner) (Match, error) {
|
||||
var m Match
|
||||
var mp, sp, v string
|
||||
err := r.Scan(&m.ID, &m.PairID, &m.RelSlave, &m.RelMaster, &m.Kind, &m.Status, &m.MasterIno, &m.MasterSize, &m.MasterMtime,
|
||||
&m.SlaveIno, &m.SlaveSize, &m.SlaveMtime, &mp, &sp, &v, &m.Note, &m.Reason, &m.Ignored, &m.UpdatedAt)
|
||||
&m.SlaveIno, &m.SlaveSize, &m.SlaveMtime, &mp, &sp, &v, &m.Note, &m.Reason, &m.Missing, &m.Ignored, &m.UpdatedAt)
|
||||
if mp != "" {
|
||||
m.MasterProbe = json.RawMessage(mp)
|
||||
}
|
||||
@@ -348,7 +349,7 @@ func (s *Store) AllMatches(ctx context.Context, pairID int64) (map[string]Match,
|
||||
}
|
||||
|
||||
type MatchFilter struct {
|
||||
Status string // a status, "review" (different+similar), "ignored" or ""
|
||||
Status string // a status, "review" (different+similar), "upgrade", "missing[_album|_artist]", "ignored" or ""
|
||||
Query string
|
||||
IDs []int64 // restrict to these matches (at most maxIDFilter)
|
||||
Limit int
|
||||
@@ -367,6 +368,11 @@ func matchWhere(pairID int64, f MatchFilter) (string, []any) {
|
||||
where = append(where, "ignored = 1")
|
||||
case "upgrade":
|
||||
where = append(where, "status IN ('different','similar') AND ignored = 0 AND "+upgradeCond)
|
||||
case "missing":
|
||||
where = append(where, "status = 'unmatched' AND missing != '' AND ignored = 0")
|
||||
case "missing_album", "missing_artist":
|
||||
where = append(where, "status = 'unmatched' AND missing = ? AND ignored = 0")
|
||||
args = append(args, strings.TrimPrefix(f.Status, "missing_"))
|
||||
default:
|
||||
where = append(where, "status = ? AND ignored = 0")
|
||||
args = append(args, f.Status)
|
||||
@@ -508,15 +514,15 @@ func (s *Store) IdenticalIDs(ctx context.Context, pairID int64) ([]int64, error)
|
||||
func (s *Store) UpsertMatch(ctx context.Context, m *Match) error {
|
||||
nullable := func(r json.RawMessage) string { return string(r) }
|
||||
_, err := s.DB.ExecContext(ctx, `INSERT INTO matches (pair_id, rel_slave, rel_master, kind, status, master_ino, master_size,
|
||||
master_mtime, slave_ino, slave_size, slave_mtime, master_probe, slave_probe, verdict, note, reason, ignored, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
master_mtime, slave_ino, slave_size, slave_mtime, master_probe, slave_probe, verdict, note, reason, missing, ignored, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(pair_id, rel_slave) DO UPDATE SET rel_master = excluded.rel_master, kind = excluded.kind,
|
||||
status = excluded.status, master_ino = excluded.master_ino, master_size = excluded.master_size,
|
||||
master_mtime = excluded.master_mtime, slave_ino = excluded.slave_ino, slave_size = excluded.slave_size,
|
||||
slave_mtime = excluded.slave_mtime, master_probe = excluded.master_probe, slave_probe = excluded.slave_probe,
|
||||
verdict = excluded.verdict, note = excluded.note, reason = excluded.reason, ignored = excluded.ignored, updated_at = excluded.updated_at`,
|
||||
verdict = excluded.verdict, note = excluded.note, reason = excluded.reason, missing = excluded.missing, ignored = excluded.ignored, updated_at = excluded.updated_at`,
|
||||
m.PairID, m.RelSlave, m.RelMaster, m.Kind, m.Status, m.MasterIno, m.MasterSize, m.MasterMtime, m.SlaveIno, m.SlaveSize,
|
||||
m.SlaveMtime, nullable(m.MasterProbe), nullable(m.SlaveProbe), nullable(m.Verdict), m.Note, m.Reason, m.Ignored, now())
|
||||
m.SlaveMtime, nullable(m.MasterProbe), nullable(m.SlaveProbe), nullable(m.Verdict), m.Note, m.Reason, m.Missing, m.Ignored, now())
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -579,12 +585,13 @@ type Summary struct {
|
||||
Review int `json:"review"`
|
||||
Upgrades int `json:"upgrades"`
|
||||
Ignored int `json:"ignored"`
|
||||
Missing map[string]int `json:"missing"` // slave-only songs by what the master has: album, artist
|
||||
ReclaimableSize int64 `json:"reclaimable_bytes"`
|
||||
LinkedSize int64 `json:"linked_bytes"`
|
||||
}
|
||||
|
||||
func (s *Store) Summary(ctx context.Context, pairID int64) (Summary, error) {
|
||||
sum := Summary{Counts: map[string]int{}}
|
||||
sum := Summary{Counts: map[string]int{}, Missing: map[string]int{}}
|
||||
rows, err := s.DB.QueryContext(ctx, `SELECT status, ignored, COUNT(*), COALESCE(SUM(slave_size), 0) FROM matches WHERE pair_id = ? GROUP BY status, ignored`, pairID)
|
||||
if err != nil {
|
||||
return sum, err
|
||||
@@ -617,7 +624,24 @@ func (s *Store) Summary(ctx context.Context, pairID int64) (Summary, error) {
|
||||
}
|
||||
err = s.DB.QueryRowContext(ctx, `SELECT COUNT(*) FROM matches WHERE pair_id = ? AND ignored = 0
|
||||
AND status IN ('different','similar') AND `+upgradeCond, pairID).Scan(&sum.Upgrades)
|
||||
return sum, err
|
||||
if err != nil {
|
||||
return sum, err
|
||||
}
|
||||
mrows, err := s.DB.QueryContext(ctx, `SELECT missing, COUNT(*) FROM matches WHERE pair_id = ? AND ignored = 0
|
||||
AND status = 'unmatched' AND missing != '' GROUP BY missing`, pairID)
|
||||
if err != nil {
|
||||
return sum, err
|
||||
}
|
||||
defer mrows.Close()
|
||||
for mrows.Next() {
|
||||
var k string
|
||||
var n int
|
||||
if err := mrows.Scan(&k, &n); err != nil {
|
||||
return sum, err
|
||||
}
|
||||
sum.Missing[k] = n
|
||||
}
|
||||
return sum, mrows.Err()
|
||||
}
|
||||
|
||||
// ---- operations ----
|
||||
|
||||
@@ -140,6 +140,9 @@ var migrations = []string{
|
||||
// 10: library type (music or soundtracks) and the step-by-step fixer's progress.
|
||||
`ALTER TABLE pairs ADD COLUMN library_type TEXT NOT NULL DEFAULT 'music';
|
||||
ALTER TABLE pairs ADD COLUMN fixer_state TEXT NOT NULL DEFAULT '';`,
|
||||
|
||||
// 11: slave-only songs whose album or artist the master has.
|
||||
`ALTER TABLE matches ADD COLUMN missing TEXT NOT NULL DEFAULT '';`,
|
||||
}
|
||||
|
||||
// SchemaVersion is the newest database schema this build knows.
|
||||
|
||||
Reference in new issue
Block a user