Add the step-by-step fixer and library types
Each pair is a Music (Artist/Album/01 Title) or Soundtracks (Album/01 Title) library. The new Fixer tab takes the master, then the slave, through four reviewed steps: folder structure, album tags (optional MusicBrainz lookup; the slave reuses the fixed master's tags), folder names and file names, then links the two libraries. Every fix is an undoable operation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b11d0806c1
commit
428935dc58
19 files changed
+3503
-23
No files matched your search
+28
-16
@@ -154,6 +154,8 @@ type Pair struct {
|
||||
AutoOrganize string `json:"auto_organize"` // off, slave, master or both
|
||||
OrganizeDiscDigits int `json:"-"` // superseded by OrganizeConfig; read to convert old settings
|
||||
OrganizeConfig json.RawMessage `json:"-"` // naming settings (engine.NamingConfig)
|
||||
LibraryType string `json:"library_type"` // music or soundtracks
|
||||
FixerState json.RawMessage `json:"-"` // step-by-step fixer progress (engine.FixerState)
|
||||
LastScanAt *int64 `json:"last_scan_at"`
|
||||
LastScanError string `json:"last_scan_error"`
|
||||
LastScanWarnings []Warning `json:"last_scan_warnings"`
|
||||
@@ -163,18 +165,18 @@ type Pair struct {
|
||||
const pairCols = `id, name, master_root, slave_root, quarantine_dir, extensions, auto_link_identical,
|
||||
scan_interval_minutes, retention_days, settle_minutes, approx_matching, approx_threshold, organize_template,
|
||||
organize_multidisc, auto_organize, organize_disc_digits, organize_config, last_scan_at, last_scan_error,
|
||||
last_scan_warnings, created_at`
|
||||
last_scan_warnings, created_at, library_type, fixer_state`
|
||||
|
||||
type scanner interface{ Scan(...any) error }
|
||||
|
||||
func scanPair(r scanner) (Pair, error) {
|
||||
var p Pair
|
||||
var exts, warns, orgCfg string
|
||||
var exts, warns, orgCfg, fixer string
|
||||
var last sql.NullInt64
|
||||
err := r.Scan(&p.ID, &p.Name, &p.MasterRoot, &p.SlaveRoot, &p.QuarantineDir, &exts, &p.AutoLinkIdentical,
|
||||
&p.ScanIntervalMinutes, &p.RetentionDays, &p.SettleMinutes, &p.ApproxMatching, &p.ApproxThreshold, &p.OrganizeTemplate,
|
||||
&p.OrganizeMultiDisc, &p.AutoOrganize, &p.OrganizeDiscDigits, &orgCfg, &last, &p.LastScanError,
|
||||
&warns, &p.CreatedAt)
|
||||
&warns, &p.CreatedAt, &p.LibraryType, &fixer)
|
||||
if err != nil {
|
||||
return p, err
|
||||
}
|
||||
@@ -186,6 +188,9 @@ func scanPair(r scanner) (Pair, error) {
|
||||
if orgCfg != "" {
|
||||
p.OrganizeConfig = json.RawMessage(orgCfg)
|
||||
}
|
||||
if fixer != "" {
|
||||
p.FixerState = json.RawMessage(fixer)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
@@ -219,11 +224,11 @@ func (s *Store) SavePair(ctx context.Context, p *Pair) error {
|
||||
if p.ID == 0 {
|
||||
res, err := s.DB.ExecContext(ctx, `INSERT INTO pairs (name, master_root, slave_root, quarantine_dir, extensions,
|
||||
auto_link_identical, scan_interval_minutes, retention_days, settle_minutes, approx_matching, approx_threshold,
|
||||
organize_template, organize_multidisc, auto_organize, organize_disc_digits, organize_config, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
organize_template, organize_multidisc, auto_organize, organize_disc_digits, organize_config, library_type, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
p.Name, p.MasterRoot, p.SlaveRoot, p.QuarantineDir, exts, p.AutoLinkIdentical,
|
||||
p.ScanIntervalMinutes, p.RetentionDays, p.SettleMinutes, p.ApproxMatching, p.ApproxThreshold,
|
||||
p.OrganizeTemplate, p.OrganizeMultiDisc, p.AutoOrganize, p.OrganizeDiscDigits, string(p.OrganizeConfig), now())
|
||||
p.OrganizeTemplate, p.OrganizeMultiDisc, p.AutoOrganize, p.OrganizeDiscDigits, string(p.OrganizeConfig), p.LibraryType, now())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -233,10 +238,16 @@ func (s *Store) SavePair(ctx context.Context, p *Pair) error {
|
||||
_, err := s.DB.ExecContext(ctx, `UPDATE pairs SET name = ?, master_root = ?, slave_root = ?, quarantine_dir = ?,
|
||||
extensions = ?, auto_link_identical = ?, scan_interval_minutes = ?, retention_days = ?, settle_minutes = ?,
|
||||
approx_matching = ?, approx_threshold = ?, organize_template = ?, organize_multidisc = ?, auto_organize = ?,
|
||||
organize_disc_digits = ?, organize_config = ? WHERE id = ?`,
|
||||
organize_disc_digits = ?, organize_config = ?, library_type = ? WHERE id = ?`,
|
||||
p.Name, p.MasterRoot, p.SlaveRoot, p.QuarantineDir, exts, p.AutoLinkIdentical,
|
||||
p.ScanIntervalMinutes, p.RetentionDays, p.SettleMinutes, p.ApproxMatching, p.ApproxThreshold,
|
||||
p.OrganizeTemplate, p.OrganizeMultiDisc, p.AutoOrganize, p.OrganizeDiscDigits, string(p.OrganizeConfig), p.ID)
|
||||
p.OrganizeTemplate, p.OrganizeMultiDisc, p.AutoOrganize, p.OrganizeDiscDigits, string(p.OrganizeConfig), p.LibraryType, p.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// SetFixerState stores the fixer's progress for a pair.
|
||||
func (s *Store) SetFixerState(ctx context.Context, id int64, raw []byte) error {
|
||||
_, err := s.DB.ExecContext(ctx, `UPDATE pairs SET fixer_state = ? WHERE id = ?`, string(raw), id)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -664,14 +675,15 @@ type Operation struct {
|
||||
|
||||
// Operation actions.
|
||||
const (
|
||||
ActLink = "link" // slave replaced by hardlink of master
|
||||
ActPromote = "promote" // master replaced by slave's file, then linked
|
||||
ActBuild = "build" // master file linked into the slave (fresh library)
|
||||
ActRemove = "remove" // slave hardlink removed (master copy kept)
|
||||
ActRename = "rename" // slave folder renamed to the master's spelling
|
||||
ActTags = "tags" // tags of a file edited
|
||||
ActMove = "move" // file renamed/moved by the organizer
|
||||
ActCover = "cover" // album folder image written
|
||||
ActLink = "link" // slave replaced by hardlink of master
|
||||
ActPromote = "promote" // master replaced by slave's file, then linked
|
||||
ActBuild = "build" // master file linked into the slave (fresh library)
|
||||
ActRemove = "remove" // slave hardlink removed (master copy kept)
|
||||
ActRename = "rename" // slave folder renamed to the master's spelling
|
||||
ActTags = "tags" // tags of a file edited
|
||||
ActMove = "move" // file renamed/moved by the organizer
|
||||
ActCover = "cover" // album folder image written
|
||||
ActMoveDir = "move-dir" // folder moved by the fixer (structure or folder names)
|
||||
)
|
||||
|
||||
// Operation statuses.
|
||||
|
||||
Reference in new issue
Block a user