package engine import ( "fmt" "path/filepath" "strings" ) // A slave-only song is "missing from the master" when the master has its // album folder (the song was probably never added there) or at least its // artist folder (the album is missing). Folders are compared with disc // folders ignored and release labels such as "Deluxe Edition" stripped, so // "Robot Face (Deluxe)" in the slave finds "Robot Face" in the master. // Missing levels. const ( MissingAlbum = "album" MissingArtist = "artist" ) // folderNorm is a folder name reduced to letters and digits, release labels removed. func folderNorm(name string) string { if w := words(stripRelease(name)); len(w) > 0 { return strings.Join(w, "") } return strings.ToLower(name) } // folderKeys returns the normalized artist key and album key of a song's // folders; the album key is empty for a song directly in the artist folder. func folderKeys(rel string) (artist, album string) { var parts []string for _, p := range strings.Split(filepath.Dir(rel), string(filepath.Separator)) { if p != "." && !discFolder.MatchString(p) { parts = append(parts, folderNorm(p)) } } if len(parts) == 0 { return "", "" } if len(parts) > 1 { album = strings.Join(parts, "/") } return parts[0], album } // masterFolders indexes the master's artist and album folders by key, to // their real spelling. type masterFolders struct { artists map[string]string albums map[string]string } func buildMasterFolders(rels []string) masterFolders { mf := masterFolders{artists: map[string]string{}, albums: map[string]string{}} for _, rel := range rels { artist, album := folderKeys(rel) if artist == "" { continue } if _, ok := mf.artists[artist]; !ok { mf.artists[artist] = firstComponent(rel) } if album != "" { if _, ok := mf.albums[album]; !ok { d := filepath.Dir(rel) for d != "." && discFolder.MatchString(filepath.Base(d)) { d = filepath.Dir(d) } mf.albums[album] = d } } } return mf } // missing reports whether the master has a slave-only song's album or // artist, with a reason naming the master's folder. func (mf masterFolders) missing(relSlave string) (level, reason string) { artist, album := folderKeys(relSlave) if artist == "" { return "", "" } if d, ok := mf.albums[album]; ok && album != "" { return MissingAlbum, fmt.Sprintf("the master has the album %q but not this song", d) } if d, ok := mf.artists[artist]; ok { return MissingArtist, fmt.Sprintf("the master has the artist %q but not this album", d) } return "", "" }