package main import ( "io/ioutil" "log" "os" "path" "sort" "strings" "github.com/dhowden/tag" ) type metadata struct { Title string Artist string Album string Track int } func readMetadata(f *os.File) *metadata { var metadata metadata m, err := tag.ReadFrom(f) if err != nil || m.Title() == "" { metadata.Title = path.Base(f.Name()) } else { metadata.Title = m.Title() metadata.Artist = m.Artist() metadata.Album = m.Album() metadata.Track, _ = m.Track() } return &metadata } type libraryEntry struct { File os.FileInfo Metadata *metadata } func (e *libraryEntry) String() string { if e.Metadata.Title != "" { if e.Metadata.Artist != "" { return e.Metadata.Artist + " - " + e.Metadata.Title } return e.Metadata.Title } return e.File.Name() } func scanFolder(scanPath string) []*libraryEntry { files, err := ioutil.ReadDir(scanPath) if err != nil { log.Fatalf("failed to scan %s: %s", scanPath, err) } var entries []*libraryEntry for _, fileInfo := range files { b := path.Base(fileInfo.Name()) if fileInfo.IsDir() { if b != "" && (b[0] != '.' || showHiddenFolders) { entries = append(entries, &libraryEntry{File: fileInfo, Metadata: &metadata{Title: fileInfo.Name()}}) } continue } else if !supportedFormat(b) { continue } f, err := os.Open(path.Join(scanPath, fileInfo.Name())) if err != nil { continue } metadata := readMetadata(f) f.Close() entries = append(entries, &libraryEntry{File: fileInfo, Metadata: metadata}) } sort.Slice(entries, func(i, j int) bool { if entries[i].File.IsDir() != entries[j].File.IsDir() { return entries[i].File.IsDir() } if entries[i].Metadata.Album != "" && strings.ToLower(entries[i].Metadata.Album) == strings.ToLower(entries[j].Metadata.Album) && (entries[i].Metadata.Track > 0 || entries[j].Metadata.Track > 0) { return entries[i].Metadata.Track < entries[j].Metadata.Track } return strings.ToLower(entries[i].Metadata.Album) < strings.ToLower(entries[j].Metadata.Album) && strings.ToLower(entries[i].File.Name()) < strings.ToLower(entries[j].File.Name()) }) return entries }