138 lines
2.9 KiB
Go
138 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/dhowden/tag"
|
|
)
|
|
|
|
type metadata struct {
|
|
Title string
|
|
Artist string
|
|
Album string
|
|
Track int
|
|
Length time.Duration
|
|
}
|
|
|
|
func readMetadata(f *os.File) *metadata {
|
|
var metadata metadata
|
|
|
|
m, err := tag.ReadFrom(f)
|
|
if err != nil || m.Title() == "" {
|
|
metadata.Title = strings.TrimSpace(path.Base(f.Name()))
|
|
} else {
|
|
metadata.Title = strings.TrimSpace(m.Title())
|
|
metadata.Artist = strings.TrimSpace(m.Artist())
|
|
metadata.Album = strings.TrimSpace(m.Album())
|
|
metadata.Track, _ = m.Track()
|
|
|
|
/*
|
|
TODO Too slow
|
|
d := mp3.NewDecoder(f)
|
|
var frame mp3.Frame
|
|
var skipped int
|
|
for {
|
|
err = d.Decode(&frame, &skipped)
|
|
if err != nil {
|
|
break
|
|
}
|
|
|
|
metadata.Length += frame.Duration()
|
|
}
|
|
*/
|
|
}
|
|
|
|
return &metadata
|
|
}
|
|
|
|
type libraryEntry struct {
|
|
File os.FileInfo
|
|
Path string
|
|
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 strings.TrimSpace(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 {
|
|
p := path.Join(scanPath, fileInfo.Name())
|
|
|
|
b := path.Base(fileInfo.Name())
|
|
if fileInfo.IsDir() {
|
|
if b != "" && (b[0] != '.' || showHiddenFolders) {
|
|
entries = append(entries, &libraryEntry{File: fileInfo, Path: p, Metadata: &metadata{Title: strings.TrimSpace(fileInfo.Name())}})
|
|
}
|
|
|
|
continue
|
|
} else if !supportedFormat(b) {
|
|
continue
|
|
}
|
|
|
|
f, err := os.Open(p)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
metadata := readMetadata(f)
|
|
f.Close()
|
|
|
|
entries = append(entries, &libraryEntry{File: fileInfo, Path: p, 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
|
|
}
|
|
|
|
func scanFolderRecursively(path string) []*libraryEntry {
|
|
var entries []*libraryEntry
|
|
|
|
scanFiles := scanFolder(path)
|
|
for _, entry := range scanFiles {
|
|
if !entry.File.IsDir() {
|
|
continue
|
|
}
|
|
|
|
entries = append(entries, scanFolderRecursively(entry.Path)...)
|
|
}
|
|
for _, entry := range scanFiles {
|
|
if entry.File.IsDir() {
|
|
continue
|
|
}
|
|
|
|
entries = append(entries, entry)
|
|
}
|
|
|
|
return entries
|
|
}
|