func ListAlbums(artist string) ([]fuse.Dirent, error) {
	db, err := bolt.Open(config.DbPath, 0600, nil)
	if err != nil {
		return nil, err
	}
	defer db.Close()
	var a []fuse.Dirent
	err = db.View(func(tx *bolt.Tx) error {
		root := tx.Bucket([]byte("Artists"))
		b := root.Bucket([]byte(artist))
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			if v == nil {
				var node fuse.Dirent
				node.Name = string(k)
				node.Type = fuse.DT_Dir
				a = append(a, node)
			} else {
				var node fuse.Dirent
				node.Name = string(k)
				node.Type = fuse.DT_File
				a = append(a, node)
			}
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return a, nil
}
ListAlbums returns all the Dirent corresponding
to Albums for a specified Artist in the database.
This is used to generate the Album listing on the
generated filesystem.
It returns nil in the second return value if there
was no error and nil if the Albums were
obtained correctly.
package store
import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/dankomiocevic/mulifs/musicmgr"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"unicode"
	"bazil.org/fuse"
	"github.com/boltdb/bolt"
	"github.com/golang/glog"
	"golang.org/x/text/transform"
	"golang.org/x/text/unicode/norm"
)
var config struct {
	DbPath string
}
type ArtistStore struct {
	ArtistName   string
	ArtistPath   string
	ArtistAlbums []string
}
type AlbumStore struct {
	AlbumName string
	AlbumPath string
}
type SongStore struct {
	SongName     string
	SongPath     string
	SongFullPath string
	Playlists    []string
}
func InitDB(path string) error {
	db, err := bolt.Open(path, 0600, nil)
	if err != nil {
		return err
	}
	defer db.Close()
	err = db.Update(func(tx *bolt.Tx) error {
		_, err = tx.CreateBucketIfNotExists([]byte("Artists"))
		if err != nil {
			glog.Errorf("Error creating bucket: %s", err)
			return fmt.Errorf("Error creating bucket: %s", err)
		}
		return nil
	})
	if err != nil {
		return err
	}
	config.DbPath = path
	return nil
}
func isMn(r rune) bool {
	return unicode.Is(unicode.Mn, r)
}
func GetCompatibleString(name string) string {
	name = strings.Replace(name, "&", "and", -1)
	t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
	result, _, _ := transform.String(t, name)
	s, _ := regexp.Compile(`\s+`)
	result = s.ReplaceAllString(result, "_")
	r, _ := regexp.Compile(`\W`)
	result = r.ReplaceAllString(result, "")
	return result
}
func StoreNewSong(song *musicmgr.FileTags, path string) error {
	db, err := bolt.Open(config.DbPath, 0600, nil)
	if err != nil {
		return err
	}
	defer db.Close()
	var artistStore ArtistStore
	var albumStore AlbumStore
	var songStore SongStore
	err = db.Update(func(tx *bolt.Tx) error {
		artistsBucket, updateError := tx.CreateBucketIfNotExists([]byte("Artists"))
		if updateError != nil {
			glog.Errorf("Error creating bucket: %s", updateError)
			return fmt.Errorf("Error creating bucket: %s", updateError)
		}
		artistPath := GetCompatibleString(song.Artist)
		albumPath := GetCompatibleString(song.Album)
		songPath := GetCompatibleString(song.Title)
		artistBucket, updateError := artistsBucket.CreateBucketIfNotExists([]byte(artistPath))
		if updateError != nil {
			glog.Errorf("Error creating bucket: %s", updateError)
			return fmt.Errorf("Error creating bucket: %s", updateError)
		}
		descValue := artistBucket.Get([]byte(".description"))
		if descValue == nil {
			artistStore.ArtistName = song.Artist
			artistStore.ArtistPath = artistPath
			artistStore.ArtistAlbums = []string{albumPath}
		} else {
			err := json.Unmarshal(descValue, &artistStore)
			if err != nil {
				artistStore.ArtistName = song.Artist
				artistStore.ArtistPath = artistPath
				artistStore.ArtistAlbums = []string{albumPath}
			}
			var found bool = false
			for _, a := range artistStore.ArtistAlbums {
				if a == albumPath {
					found = true
					break
				}
			}
			if found == false {
				artistStore.ArtistAlbums = append(artistStore.ArtistAlbums, albumPath)
			}
		}
		encoded, err := json.Marshal(artistStore)
		if err != nil {
			return err
		}
		artistBucket.Put([]byte(".description"), encoded)
		albumBucket, updateError := artistBucket.CreateBucketIfNotExists([]byte(albumPath))
		if updateError != nil {
			glog.Errorf("Error creating bucket: %s", updateError)
			return fmt.Errorf("Error creating bucket: %s", updateError)
		}
		albumStore.AlbumName = song.Album
		albumStore.AlbumPath = albumPath
		encoded, err = json.Marshal(albumStore)
		if err != nil {
			return err
		}
		albumBucket.Put([]byte(".description"), encoded)
		_, file := filepath.Split(path)
		extension := filepath.Ext(file)
		songStore.SongName = song.Title
		songStore.SongPath = songPath + extension
		songStore.SongFullPath = path
		encoded, err = json.Marshal(songStore)
		if err != nil {
			return err
		}
		albumBucket.Put([]byte(songPath+extension), encoded)
		return nil
	})
	return nil
}
func ListArtists() ([]fuse.Dirent, error) {
	db, err := bolt.Open(config.DbPath, 0600, nil)
	if err != nil {
		return nil, err
	}
	defer db.Close()
	var a []fuse.Dirent
	err = db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("Artists"))
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			if v == nil {
				var node fuse.Dirent
				node.Name = string(k)
				node.Type = fuse.DT_Dir
				a = append(a, node)
			} else {
				var node fuse.Dirent
				node.Name = string(k)
				node.Type = fuse.DT_File
				a = append(a, node)
			}
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return a, nil
}