ditty/library.go

159 lines
3.4 KiB
Go
Raw Normal View History

2020-01-08 23:38:41 +00:00
package main
import (
2021-08-17 20:21:06 +00:00
"fmt"
"io/fs"
2020-01-08 23:38:41 +00:00
"io/ioutil"
"os"
"path"
"sort"
"strings"
2020-04-24 22:39:02 +00:00
"time"
2020-01-08 23:38:41 +00:00
"github.com/dhowden/tag"
)
2021-09-02 20:06:27 +00:00
type metadata struct {
2020-01-08 23:38:41 +00:00
Title string
Artist string
Album string
Track int
2020-04-24 22:39:02 +00:00
Length time.Duration
2020-01-08 23:38:41 +00:00
}
2021-09-02 20:06:27 +00:00
func readMetadata(f *os.File) *metadata {
var Metadata metadata
2020-01-08 23:38:41 +00:00
m, err := tag.ReadFrom(f)
if err != nil || m.Title() == "" {
Metadata.Title = strings.TrimSpace(path.Base(f.Name()))
2020-01-08 23:38:41 +00:00
} else {
Metadata.Title = strings.TrimSpace(m.Title())
Metadata.Artist = strings.TrimSpace(m.Artist())
Metadata.Album = strings.TrimSpace(m.Album())
Metadata.Track, _ = m.Track()
2020-04-24 22:39:02 +00:00
/*
TODO Too slow
d := mp3.NewDecoder(f)
var frame mp3.Frame
var skipped int
for {
err = d.Decode(&frame, &skipped)
if err != nil {
break
}
2021-09-02 20:06:27 +00:00
metadata.Length += frame.Duration()
2020-04-24 22:39:02 +00:00
}
*/
2020-01-08 23:38:41 +00:00
}
return &Metadata
2020-01-08 23:38:41 +00:00
}
2021-09-02 20:06:27 +00:00
type libraryEntry struct {
Name string
IsDir bool
Mode fs.FileMode
2020-01-28 17:26:44 +00:00
Path string
2020-05-25 05:14:28 +00:00
RealPath string
2021-09-02 20:06:27 +00:00
Metadata *metadata
2020-01-08 23:38:41 +00:00
}
2021-09-02 20:06:27 +00:00
func (e *libraryEntry) String() string {
2020-01-08 23:38:41 +00:00
if e.Metadata.Title != "" {
if e.Metadata.Artist != "" {
return e.Metadata.Artist + " - " + e.Metadata.Title
}
return e.Metadata.Title
}
return strings.TrimSpace(e.Name)
2020-01-08 23:38:41 +00:00
}
2021-09-02 20:06:27 +00:00
func scanFolder(scanPath string) []*libraryEntry {
2020-01-08 23:38:41 +00:00
files, err := ioutil.ReadDir(scanPath)
if err != nil {
2021-08-17 20:21:06 +00:00
panic(fmt.Sprintf("failed to scan %s: %s", scanPath, err))
2020-01-08 23:38:41 +00:00
}
2021-09-02 20:06:27 +00:00
var entries []*libraryEntry
2020-01-08 23:38:41 +00:00
for _, fileInfo := range files {
2020-01-28 17:26:44 +00:00
p := path.Join(scanPath, fileInfo.Name())
2020-05-25 05:14:28 +00:00
var r string
2020-01-28 17:26:44 +00:00
2020-05-25 05:14:28 +00:00
if fileInfo.Mode()&os.ModeSymlink != 0 {
r, err = os.Readlink(p)
if err != nil {
continue
}
if !path.IsAbs(r) {
r = path.Join(scanPath, r)
}
} else {
r = p
}
b := path.Base(p)
if fileInfo.IsDir() || fileInfo.Mode()&os.ModeSymlink != 0 {
if b != "" && (b[0] != '.' || showHiddenFolders) {
2021-09-02 20:06:27 +00:00
entries = append(entries, &libraryEntry{Name: fileInfo.Name(), IsDir: fileInfo.IsDir(), Mode: fileInfo.Mode(), Path: p, RealPath: r, Metadata: &metadata{Title: strings.TrimSpace(fileInfo.Name())}})
}
2020-01-08 23:38:41 +00:00
continue
} else if !supportedFormat(b) {
2020-01-08 23:38:41 +00:00
continue
}
2020-05-25 05:14:28 +00:00
f, err := os.Open(r)
2020-01-08 23:38:41 +00:00
if err != nil {
continue
}
Metadata := readMetadata(f)
2020-01-08 23:38:41 +00:00
f.Close()
2021-09-02 20:06:27 +00:00
entries = append(entries, &libraryEntry{Name: fileInfo.Name(), IsDir: fileInfo.IsDir(), Mode: fileInfo.Mode(), Path: p, RealPath: r, Metadata: Metadata})
2020-01-08 23:38:41 +00:00
}
sort.Slice(entries, func(i, j int) bool {
iDir := entries[i].IsDir || entries[i].Mode&os.ModeSymlink != 0
jDir := entries[j].IsDir || entries[j].Mode&os.ModeSymlink != 0
2020-05-25 05:14:28 +00:00
if iDir != jDir {
return iDir
2020-01-08 23:38:41 +00:00
}
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].Name) < strings.ToLower(entries[j].Name)
2020-01-08 23:38:41 +00:00
})
return entries
}
2020-01-28 17:26:44 +00:00
2021-09-02 20:06:27 +00:00
func scanFolderRecursively(path string) []*libraryEntry {
var entries []*libraryEntry
2020-01-28 17:26:44 +00:00
scanFiles := scanFolder(path)
for _, entry := range scanFiles {
if !entry.IsDir && entry.Mode&os.ModeSymlink == 0 {
2020-01-28 17:26:44 +00:00
continue
}
2020-05-25 05:14:28 +00:00
entries = append(entries, scanFolderRecursively(entry.RealPath)...)
2020-01-28 17:26:44 +00:00
}
for _, entry := range scanFiles {
if entry.IsDir || entry.Mode&os.ModeSymlink != 0 {
2020-01-28 17:26:44 +00:00
continue
}
entries = append(entries, entry)
}
return entries
}