package musicindex import ( "errors" "fmt" "io/fs" "log" "os" "os/user" "slices" "strconv" "strings" "syscall" "golang.org/x/sys/unix" "github.com/walle/targz" "git.baxters.nz/jeremy/records/util" ) var TempDir = "/tmp/records" func assertAccessTo(path string) { f, err := os.Stat(path) if err != nil { if errors.Is(err, fs.ErrNotExist) { util.Die("no such file or directory: %s", path) } util.Die("cannot stat %s: %s", path, err.Error()) } if !f.IsDir() { util.Die("not a directory: %s", path) } if unix.Access(path, unix.R_OK) == nil { return } procUser, err := user.Current() if err != nil { util.Die("cannot access directory: %s", path) return } groupObject, err := user.LookupGroupId(strconv.Itoa(syscall.Getgid())) if err != nil { util.Die("cannot access directory: %s", path) return } util.Die("cannot access directory %s\nrepair all directories with: chown -R %s:%s %s", path, procUser.Username, groupObject.Name, mediaDir) } func indexArtists() { entries, err := util.Dirents(mediaDir) if err != nil { util.Die("%s", err.Error()) } err = os.RemoveAll(TempDir) if err != nil { util.Die("cannot remove temporary directory %s: %s", TempDir, err.Error()) } err = os.Mkdir(TempDir, 0711) if err != nil { util.Die("cannot make temporary directory %s: %s", TempDir, err.Error()) } artists = make(map[string]Artist) for _, artist := range entries { stat, err := os.Stat(mediaDir + "/" + artist) if err != nil { continue } if !stat.IsDir() { continue } var a Artist a.Name = artist a.Albums = indexAlbums(&a) a.Songs = 0 a.Valid = true for _, album := range a.Albums { a.Songs += len(album.TrackFiles) } artists[a.Name] = a } } func indexAlbums(artist *Artist) (albums map[string]Album) { artistDir := mediaDir + "/" + artist.Name assertAccessTo(artistDir) entries, err := util.Dirents(artistDir) if err != nil { util.Die("%s", err.Error()) } albums = make(map[string]Album) for _, albumName := range entries { util.Iprint("* index %s - %s\r", artist.Name, albumName) albumDir := artistDir + "/" + albumName album, err := indexAlbum(artist, albumName, albumDir) if err != nil { log.Printf("warn: skipping inaccessible album %s: %s", albumName, err.Error()) } else { albums[albumName] = album } } return } func indexAlbum(artist *Artist, albumName, albumDir string) (a Album, err error) { stat, err := os.Stat(albumDir) if err != nil { return } if !stat.IsDir() { err = errors.New("not a directory") } a.Artist = artist a.Name = albumName a.HasCover = false a.CoverFile = "" err = unix.Access(albumDir + "/" + defaultCoverFile, unix.R_OK) if err == nil { a.HasCover = true a.CoverFile = defaultCoverFile } var tracks []string albumDirEntries, err := util.Dirents(albumDir) if err != nil { return } for _, track := range albumDirEntries { if strings.HasSuffix(track, trackFileExtension) { tracks = append(tracks, track) } } slices.Sort(tracks) a.TrackFiles = tracks hash := util.HashOf(a.Artist.Name + "::" + a.Name) path := TempDir + "/" + hash if _, err = os.Stat(path); errors.Is(err, os.ErrNotExist) { a.Tarball = path } else if err == nil { a.Tarball = "" for i := range 9 { if _, err = os.Stat(fmt.Sprintf("%s.%d", path, i)); err == nil { continue } else if errors.Is(err, os.ErrNotExist) { a.Tarball = path } else { err = errors.New("cannot create tarball in filesystem: " + err.Error()) return } } if len(a.Tarball) == 0 { err = errors.New("cannot create tarball in filesystem; " + "please clean out " + TempDir) return } } else { err = errors.New("cannot create tarball in filesystem: " + err.Error()) return } a.Tarball = path err = targz.Compress(a.Directory(), a.Tarball) if err != nil { return } tarball, err := os.Stat(a.Tarball) if err != nil { return } a.TarballSize = tarball.Size() err = nil return }