summaryrefslogtreecommitdiff
path: root/files
diff options
context:
space:
mode:
authorjakka <jakkadoujin@gmail.com>2025-04-12 14:44:39 +0300
committerjakka <jakkadoujin@gmail.com>2025-04-12 14:44:39 +0300
commit1675136966473083e970badfe92433a05d2d7fee (patch)
tree5cd22f2ee065514a1c5db508f6d6a5ed3757c454 /files
parent6f3071a58211598ee23d1ef6ab17156e15b2e27b (diff)
implemented database and concurrent file scanning
Diffstat (limited to 'files')
-rw-r--r--files/database.go126
-rw-r--r--files/index.go53
-rw-r--r--files/types.go19
3 files changed, 196 insertions, 2 deletions
diff --git a/files/database.go b/files/database.go
new file mode 100644
index 0000000..24bd5af
--- /dev/null
+++ b/files/database.go
@@ -0,0 +1,126 @@
+package files
+
+import (
+ "context"
+ "crypto/sha256"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/fs"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "strings"
+
+ "github.com/tidwall/buntdb"
+)
+
+func evaluateFile(database *buntdb.DB, hashsum string, filedata FileInfo) error {
+ return database.View(func(tx *buntdb.Tx) error {
+ data, err := tx.Get(hashsum)
+ if err != nil {
+ return err
+ }
+
+ var info FileInfo
+ if err := json.Unmarshal([]byte(data), &info); err != nil {
+ return err
+ }
+ if info == filedata {
+ return UpToDate
+ }
+ switch info.Modtime == filedata.Modtime {
+ case true:
+ if info.AbsPath == filedata.AbsPath {
+ if info.Process {
+ return NeedsReencode
+ }
+ return UpToDate
+ }
+ if info.Process {
+ return NeedsReencode
+ }
+ return MovedFile
+ default:
+ return nil
+ }
+ })
+}
+
+func getEncoderVer(path string) (string, error) {
+ out, err := exec.Command("metaflac", "--show-vendor-tag", path).Output()
+ if err != nil {
+ return "", err
+ }
+
+ r := regexp.MustCompile("libFLAC \\d\\.\\d\\.\\d")
+ encoder := r.FindString(string(out))
+ switch encoder {
+ case "":
+ return "", nil
+ default:
+ return strings.Split(encoder, " ")[1], nil
+ }
+}
+
+func updateFile(database *buntdb.DB, hashsum string, filedata FileInfo) error {
+ return database.Update(func(tx *buntdb.Tx) error {
+ data, err := json.Marshal(filedata)
+ if err != nil {
+ return err
+ }
+ if _, _, err := tx.Set(hashsum, string(data), nil); err != nil {
+ return err
+ }
+ return nil
+ })
+}
+
+func ProcessFile(ctx context.Context, path string, info fs.DirEntry) error {
+ tmp, err := info.Info()
+ if err != nil {
+ return err
+ }
+
+ modtime := tmp.ModTime()
+
+ database := ctx.Value("database").(*buntdb.DB)
+
+ file, err := os.Open(path)
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ hash := sha256.New()
+ if _, err := io.Copy(hash, file); err != nil {
+ return err
+ }
+ hashsum := fmt.Sprintf("%x", hash.Sum(nil))
+
+ abspath, err := filepath.Abs(path)
+ if err != nil {
+ return err
+ }
+
+ encoder, err := getEncoderVer(path)
+
+ filedata := FileInfo{AbsPath: abspath, Modtime: modtime, Encoder: encoder, Process: true}
+
+ err = evaluateFile(database, hashsum, filedata)
+ switch err {
+ case MovedFile, UpToDate:
+ if filedata.Encoder == ctx.Value("encoder").(string) {
+ return nil
+ }
+ case buntdb.ErrNotFound, NeedsReencode:
+
+ default:
+ return err
+ }
+ if err := updateFile(database, hashsum, filedata); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/files/index.go b/files/index.go
index e02e4ba..ea05dc4 100644
--- a/files/index.go
+++ b/files/index.go
@@ -1,7 +1,56 @@
-package main
+package files
-import "context"
+import (
+ "context"
+ "io/fs"
+ "path/filepath"
+ "time"
+
+ "github.com/briandowns/spinner"
+ "golang.org/x/sync/errgroup"
+)
func IndexFlacs(ctx context.Context) error {
+ spin := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
+ spin.Suffix = " Indexing flacs..."
+
+ spin.Start()
+
+ wg := new(errgroup.Group)
+ wg.SetLimit(100)
+
+ if err := filepath.WalkDir(ctx.Value("path").(string), func(path string, info fs.DirEntry, err error) error {
+ select {
+ case <-ctx.Done():
+ spin.FinalMSG = "Stopping...\n"
+ spin.Stop()
+ return nil
+ default:
+ if err != nil {
+ return err
+ }
+ if !info.IsDir() {
+ if filepath.Ext(path) == ".flac" {
+ wg.Go(func() error {
+ select {
+ case <-ctx.Done():
+ return nil
+ default:
+ return ProcessFile(ctx, path, info)
+ }
+ })
+ }
+
+ }
+ return nil
+ }
+
+ }); err != nil {
+ return err
+ }
+
+ wg.Wait()
+ spin.FinalMSG = "Done indexing flacs"
+ spin.Stop()
return nil
}
diff --git a/files/types.go b/files/types.go
new file mode 100644
index 0000000..54d8563
--- /dev/null
+++ b/files/types.go
@@ -0,0 +1,19 @@
+package files
+
+import (
+ "errors"
+ "time"
+)
+
+var (
+ UpToDate = errors.New("file is up to date")
+ MovedFile = errors.New("file was moved")
+ NeedsReencode = errors.New("file needs to be reencoded")
+)
+
+type FileInfo struct {
+ AbsPath string `json:"abspath"`
+ Modtime time.Time `json:"modtime"`
+ Encoder string `json:"encoder"`
+ Process bool `json:"process"`
+}