summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjakka <jakkadoujin@gmail.com>2025-06-04 17:07:16 +0300
committerjakka <jakkadoujin@gmail.com>2025-06-04 17:07:16 +0300
commit5a39e523da7f4fe2c9c1e6c369a366162be122fa (patch)
tree033df21a928cf883549bd2fab71a0fa02dbfb3c8
parent9b50859cd0aa3832500fe4ea22e3b4e967d6032a (diff)
started rewriting
-rw-r--r--.gitignore7
-rw-r--r--Cargo.lock319
-rw-r--r--Cargo.toml11
-rw-r--r--cmd/cmd.go82
-rw-r--r--cmd/init.go84
-rw-r--r--cmd/main.go5
-rw-r--r--files/database.go172
-rw-r--r--files/operations.go182
-rw-r--r--files/types.go17
-rw-r--r--go.mod34
-rw-r--r--go.sum68
-rw-r--r--src/flac.rs130
-rw-r--r--src/main.rs5
13 files changed, 468 insertions, 648 deletions
diff --git a/.gitignore b/.gitignore
index a80ed40..ec31677 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,10 +17,9 @@
# Dependency directories (remove the comment below to include it)
# vendor/
-# Go workspace file
-go.work
-go.work.sum
-
# env file
.env
.vscode/launch.json
+target
+*.flac
+*.wav \ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..791e294
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,319 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "addr2line"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
+[[package]]
+name = "anyhow"
+version = "1.0.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
+[[package]]
+name = "backtrace"
+version = "0.3.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
+dependencies = [
+ "addr2line",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "windows-targets",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bytemuck"
+version = "1.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c"
+
+[[package]]
+name = "cc"
+version = "1.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951"
+dependencies = [
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "cmake"
+version = "0.1.54"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "flac-bound"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e907092e9fba0d718cad77433321f524836e90a2c4cc7ab67e8fb5244047ad2"
+dependencies = [
+ "libflac-sys",
+]
+
+[[package]]
+name = "gimli"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "libc"
+version = "0.2.172"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
+
+[[package]]
+name = "libflac-sys"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01b01d08e4f670c184ffe3c3e5efbf0bd6e88c8cca796c278c86bbf49583545c"
+dependencies = [
+ "cmake",
+ "libc",
+]
+
+[[package]]
+name = "log"
+version = "0.4.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
+dependencies = [
+ "adler2",
+]
+
+[[package]]
+name = "object"
+version = "0.36.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "reencoder"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "flac-bound",
+ "symphonia",
+ "symphonia-metadata",
+ "tokio",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "symphonia"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "815c942ae7ee74737bb00f965fa5b5a2ac2ce7b6c01c0cc169bbeaf7abd5f5a9"
+dependencies = [
+ "lazy_static",
+ "symphonia-bundle-flac",
+ "symphonia-core",
+ "symphonia-metadata",
+]
+
+[[package]]
+name = "symphonia-bundle-flac"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72e34f34298a7308d4397a6c7fbf5b84c5d491231ce3dd379707ba673ab3bd97"
+dependencies = [
+ "log",
+ "symphonia-core",
+ "symphonia-metadata",
+ "symphonia-utils-xiph",
+]
+
+[[package]]
+name = "symphonia-core"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "798306779e3dc7d5231bd5691f5a813496dc79d3f56bf82e25789f2094e022c3"
+dependencies = [
+ "arrayvec",
+ "bitflags",
+ "bytemuck",
+ "lazy_static",
+ "log",
+]
+
+[[package]]
+name = "symphonia-metadata"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc622b9841a10089c5b18e99eb904f4341615d5aa55bbf4eedde1be721a4023c"
+dependencies = [
+ "encoding_rs",
+ "lazy_static",
+ "log",
+ "symphonia-core",
+]
+
+[[package]]
+name = "symphonia-utils-xiph"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "484472580fa49991afda5f6550ece662237b00c6f562c7d9638d1b086ed010fe"
+dependencies = [
+ "symphonia-core",
+ "symphonia-metadata",
+]
+
+[[package]]
+name = "tokio"
+version = "1.45.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779"
+dependencies = [
+ "backtrace",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..1017e32
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "reencoder"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+anyhow = "1.0.98"
+flac-bound = { version = "0.5.0", features = ["libflac-noogg"], default-features = false }
+symphonia = { version = "0.5.4", default-features = false, features = ["flac"] }
+symphonia-metadata = "0.5.4"
+tokio = "1.45.1"
diff --git a/cmd/cmd.go b/cmd/cmd.go
deleted file mode 100644
index 2fb598f..0000000
--- a/cmd/cmd.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package main
-
-import (
- "context"
- "os"
- "os/signal"
-
- "github.com/rosedblabs/rosedb/v2"
- "github.com/urfave/cli/v2"
-
- "github.com/justjakka/reencoder/files"
-)
-
-func runCmd(cCtx *cli.Context) error {
- ctx, err := initArgs(cCtx)
- if err != nil {
- return err
- }
-
- options := rosedb.DefaultOptions
- options.DirPath = ctx.Value("dbfile").(string)
- db, err := rosedb.Open(options)
- if err != nil {
- return err
- }
- defer db.Close()
-
- ctx = context.WithValue(ctx, "database", db)
-
- ctx, stop := signal.NotifyContext(ctx, os.Interrupt)
- defer stop()
-
- counter := int64(0)
-
- ctx = context.WithValue(ctx, "counter", &counter)
-
- if err = files.IndexFlacs(ctx); err != nil {
- return err
- }
-
- if err = files.ReencodeFlacs(ctx); err != nil {
- return err
- }
-
- if err := db.Sync(); err != nil {
- return err
- }
- return nil
-}
-
-func Start() {
- app := &cli.App{
- Name: "reencoder",
- Usage: "reencodes and stores info",
- UsageText: "reencoder /path/to/folder",
- Description: "indexes files, checks for encoder and reencodes",
- Args: true,
- ArgsUsage: "specify amusic links to download",
- Flags: []cli.Flag{
- &cli.PathFlag{
- Name: "path",
- Usage: "Path to folder with files to reencode",
- Value: ".",
- Aliases: []string{"p"},
- },
- &cli.PathFlag{
- Name: "database",
- Usage: "Path to database",
- Aliases: []string{"d"},
- },
- &cli.StringSliceFlag{
- Name: "flac",
- Usage: "Flac arguments to use when reencoding, can be used multiple times",
- Aliases: []string{"a"},
- },
- },
- Action: runCmd,
- }
- if err := app.Run(os.Args); err != nil {
- panic(err.Error())
- }
-}
diff --git a/cmd/init.go b/cmd/init.go
deleted file mode 100644
index 2852565..0000000
--- a/cmd/init.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package main
-
-import (
- "context"
- "errors"
- "os"
- "os/exec"
- "path/filepath"
- "runtime"
- "strings"
-
- "github.com/urfave/cli/v2"
-)
-
-func getLocalStorage() string {
- switch runtime.GOOS {
- case "windows":
- return os.Getenv("APPDATA")
- case "linux":
- home, _ := os.UserHomeDir()
- return filepath.Join(home, ".local", "share")
- case "darwin":
- home, _ := os.UserHomeDir()
- return filepath.Join(home, "Library", "Application Support")
- default:
- return ""
- }
-}
-
-func getDb(cCtx *cli.Context) (context.Context, error) {
- if cCtx.Path("dbfile") == "" {
- localFolder := getLocalStorage()
- if localFolder == "" {
- return context.WithValue(cCtx.Context, "dbfile", ""), errors.New("failed to locate application data folder")
- }
-
- return context.WithValue(cCtx.Context, "dbfile", filepath.Join(localFolder, "reencoder")), nil
- }
- if _, err := os.Stat(cCtx.Path("dbfile")); err != nil {
- return context.WithValue(cCtx.Context, "dbfile", ""), err
- }
- return context.WithValue(cCtx.Context, "dbfile", cCtx.Path("dbfile")), nil
-}
-
-func checkTools() error {
- if _, err := exec.LookPath("flac"); err != nil {
- return errors.New("missing flac executable")
- }
- if _, err := exec.LookPath("metaflac"); err != nil {
- return errors.New("missing metaflac executable")
- }
- return nil
-}
-
-func initArgs(cCtx *cli.Context) (context.Context, error) {
- if err := checkTools(); err != nil {
- return nil, err
- }
-
- if _, err := os.Stat(cCtx.Path("path")); err != nil {
- return nil, err
- }
-
- ctx, err := getDb(cCtx)
- if err != nil {
- return nil, err
- }
-
- ctx = context.WithValue(ctx, "path", cCtx.Path("path"))
-
- encoder, err := exec.Command("flac", "-v").Output()
- if err != nil {
- return nil, err
- }
-
- ctx = context.WithValue(ctx, "encoder", strings.ReplaceAll(strings.Split(string(encoder), " ")[1], "\n", ""))
-
- defargs := []string{"-8f", "-j4"}
-
- if cCtx.Value("flac") == nil {
- return context.WithValue(ctx, "flac", defargs), nil
- }
- return context.WithValue(ctx, "flac", cCtx.StringSlice("flac")), nil
-}
diff --git a/cmd/main.go b/cmd/main.go
deleted file mode 100644
index 52cefc8..0000000
--- a/cmd/main.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package main
-
-func main() {
- Start()
-}
diff --git a/files/database.go b/files/database.go
deleted file mode 100644
index 33c2636..0000000
--- a/files/database.go
+++ /dev/null
@@ -1,172 +0,0 @@
-package files
-
-import (
- "context"
- "encoding/json"
- "errors"
- "log"
- "os"
- "strings"
-
- "github.com/rosedblabs/rosedb/v2"
- progressbar "github.com/schollz/progressbar/v3"
- "golang.org/x/sync/errgroup"
-)
-
-func decodeDbInfo(value []byte) (*FileInfo, error) {
- var info FileInfo
- if err := json.Unmarshal(value, &info); err != nil {
- return nil, err
- }
- return &info, nil
-}
-
-func getInfoFromDb(batch *rosedb.Batch, hashsum []byte) (*FileInfo, error) {
- exist, err := batch.Exist(hashsum)
- if err != nil {
- return nil, err
- }
-
- if !exist {
- return nil, rosedb.ErrKeyNotFound
- }
-
- value, err := batch.Get(hashsum)
- if err != nil {
- return nil, err
- }
-
- info, err := decodeDbInfo(value)
- if err != nil {
- return nil, err
- }
- return info, err
-}
-
-func (filedata *FileInfo) evaluateFile(batch *rosedb.Batch, hashsum []byte, encoder string) error {
- info, err := getInfoFromDb(batch, hashsum)
- if err != nil {
- return err
- }
-
- if info.Process {
- return ReencodeNeeded
- }
- if filedata.Encoder != encoder {
- return ReencodeNeeded
- }
- if info.AbsPath != filedata.AbsPath {
- return FileMoved
- }
- return ReencodeNotNeeded
-}
-
-func (filedata *FileInfo) updateFile(batch *rosedb.Batch, hashsum []byte) error {
- encoded, err := json.Marshal(filedata)
- if err != nil {
- return err
- }
- return batch.Put(hashsum, encoded)
-}
-
-func (filedata *FileInfo) IndexFile(ctx context.Context, hashsum []byte, batch *rosedb.Batch) error {
- encoder := ctx.Value("encoder").(string)
- counter := ctx.Value("counter").(*int64)
-
- err := filedata.evaluateFile(batch, hashsum, encoder)
-
- switch err {
- case FileMoved, ReencodeNotNeeded:
- filedata.Process = false
- case ReencodeNeeded, rosedb.ErrKeyNotFound:
- *counter += 1
- default:
- return err
- }
-
- if err := filedata.updateFile(batch, hashsum); err != nil {
- return err
- }
-
- return nil
-}
-
-func ReencodeFlacs(ctx context.Context) error {
- db := ctx.Value("database").(*rosedb.DB)
- bar := progressbar.NewOptions64(
- *ctx.Value("counter").(*int64),
- progressbar.OptionSetDescription("Reencoding..."),
- progressbar.OptionShowCount(),
- )
-
- batch := db.NewBatch(rosedb.DefaultBatchOptions)
-
- wg := new(errgroup.Group)
- wg.SetLimit(4)
-
- iterOpts := rosedb.DefaultIteratorOptions
- iterOpts.ContinueOnError = true
- iter := db.NewIterator(iterOpts)
-
- for iter.Rewind(); iter.Valid(); iter.Next() {
- select {
- case <-ctx.Done():
- wg.Wait()
- if err := batch.Commit(); err != nil {
- return err
- }
- bar.Exit()
- return nil
- default:
- item := iter.Item()
- key := item.Key
- info, err := decodeDbInfo(item.Value)
- if err != nil {
- log.Println(err.Error())
- }
-
- if !strings.Contains(info.AbsPath, ctx.Value("path").(string)) {
- continue
- } else if _, err := os.Stat(info.AbsPath); errors.Is(err, os.ErrNotExist) {
- if err := batch.Delete(key); err != nil {
- log.Println(err.Error())
- }
- } else if err != nil {
- log.Println(err.Error())
- } else {
- if info.Process {
- wg.Go(func() error {
- select {
- case <-ctx.Done():
- return nil
- default:
- if err := info.reencodeFile(ctx); err != nil {
- return err
- }
- if err := batch.Delete(key); err != nil {
- return err
- }
- key, err = getSha256(info.AbsPath)
- if err != nil {
- return err
- }
- if err := info.updateFile(batch, key); err != nil {
- return err
- }
-
- bar.Add64(1)
- return nil
- }
- })
- }
- }
- }
- }
-
- wg.Wait()
- if err := batch.Commit(); err != nil {
- return err
- }
- bar.Close()
- return nil
-}
diff --git a/files/operations.go b/files/operations.go
deleted file mode 100644
index 6f44900..0000000
--- a/files/operations.go
+++ /dev/null
@@ -1,182 +0,0 @@
-package files
-
-import (
- "context"
- "crypto/sha256"
- "fmt"
- "io"
- "io/fs"
- "log"
- "os"
- "os/exec"
- "path/filepath"
- "regexp"
- "strings"
- "time"
-
- "github.com/briandowns/spinner"
- "github.com/rosedblabs/rosedb/v2"
-)
-
-func (file *FileInfo) reencodeFile(ctx context.Context) error {
- select {
- case <-ctx.Done():
- return nil
- default:
- args := ctx.Value("flac").([]string)
- if args != nil {
- args = append(args, file.AbsPath)
- cmd := exec.Command("flac", args...)
- if err := cmd.Run(); err != nil {
- if !strings.Contains(err.Error(), "interrupt") {
- log.Println(err.Error())
- return err
- }
- }
- } else {
- cmd := exec.Command("flac", "-8f", "-j4", file.AbsPath)
- if err := cmd.Run(); err != nil {
- if !strings.Contains(err.Error(), "interrupt") {
- log.Println(err.Error())
- return err
- }
- }
- }
-
- file.Encoder = ctx.Value("encoder").(string)
- file.Process = false
- 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 getInfoFromFile(path string) (*FileInfo, error) {
- var filedata FileInfo
-
- filedata.Process = true
-
- abspath, err := filepath.Abs(path)
- if err != nil {
- return nil, err
- }
-
- filedata.AbsPath = abspath
-
- encoder, err := getEncoderVer(path)
- if err != nil {
- return nil, err
- }
-
- filedata.Encoder = encoder
-
- return &filedata, nil
-}
-
-func getSha256(path string) ([]byte, error) {
- file, err := os.Open(path)
- if err != nil {
- return nil, err
- }
- defer file.Close()
-
- hash := sha256.New()
- if _, err := io.Copy(hash, file); err != nil {
- return nil, err
- }
- return hash.Sum(nil), nil
-}
-
-func updateSpinner(spin *spinner.Spinner, counter int64) {
- line := fmt.Sprintf(" Indexing flacs...\t %v", counter)
- spin.Suffix = line
-}
-
-func IndexFlacs(ctx context.Context) error {
- counter := ctx.Value("counter").(*int64)
- database := ctx.Value("database").(*rosedb.DB)
-
- batch := database.NewBatch(rosedb.DefaultBatchOptions)
-
- spin := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
-
- updateSpinner(spin, *counter)
-
- 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 filepath.SkipAll
- default:
- if !info.IsDir() {
- if filepath.Ext(path) == ".flac" {
- /* wg.Go(func() error {
- select {
- case <-ctx.Done():
- return nil
- default:
- data, err := getInfoFromFile(path)
- if err != nil {
- return err
- }
-
- hashsum, err := getSha256(path)
- if err != nil {
- return err
- }
- updateSpinner(spin, *counter)
- return data.IndexFile(ctx, hashsum, batch)
- }
- }) */
-
- data, err := getInfoFromFile(path)
- if err != nil {
- return err
- }
-
- hashsum, err := getSha256(path)
- if err != nil {
- return err
- }
- updateSpinner(spin, *counter)
- return data.IndexFile(ctx, hashsum, batch)
- }
-
- }
- return nil
- }
-
- }); err != nil {
- return err
- }
-
- /* wg.Wait() */
-
- if err := batch.Commit(); err != nil {
- return err
- }
- spin.FinalMSG = fmt.Sprintf("Done indexing flacs: \t%v to process\n", *counter)
- spin.Stop()
-
- return nil
-}
diff --git a/files/types.go b/files/types.go
deleted file mode 100644
index 7bb2f42..0000000
--- a/files/types.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package files
-
-import (
- "errors"
-)
-
-var (
- ReencodeNotNeeded = errors.New("file is up to date")
- FileMoved = errors.New("file was moved")
- ReencodeNeeded = errors.New("file needs to be reencoded")
-)
-
-type FileInfo struct {
- AbsPath string `json:"abspath"`
- Encoder string `json:"encoder"`
- Process bool `json:"process"`
-}
diff --git a/go.mod b/go.mod
deleted file mode 100644
index dcb3034..0000000
--- a/go.mod
+++ /dev/null
@@ -1,34 +0,0 @@
-module github.com/justjakka/reencoder
-
-go 1.24.2
-
-require github.com/urfave/cli/v2 v2.27.6
-
-require (
- github.com/bwmarrin/snowflake v0.3.0 // indirect
- github.com/fatih/color v1.7.0 // indirect
- github.com/gofrs/flock v0.8.1 // indirect
- github.com/google/btree v1.1.2 // indirect
- github.com/kr/pretty v0.3.1 // indirect
- github.com/mattn/go-colorable v0.1.2 // indirect
- github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
- github.com/rivo/uniseg v0.4.7 // indirect
- github.com/robfig/cron/v3 v3.0.0 // indirect
- github.com/rogpeppe/go-internal v1.13.1 // indirect
- github.com/rosedblabs/wal v1.3.8 // indirect
- github.com/stretchr/testify v1.10.0 // indirect
- github.com/valyala/bytebufferpool v1.0.0 // indirect
- golang.org/x/sys v0.31.0 // indirect
- golang.org/x/term v0.30.0 // indirect
-)
-
-require (
- github.com/briandowns/spinner v1.23.2
- github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
- github.com/rosedblabs/rosedb/v2 v2.4.0
- github.com/russross/blackfriday/v2 v2.1.0 // indirect
- github.com/schollz/progressbar/v3 v3.18.0
- github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
- golang.org/x/sync v0.13.0
-)
diff --git a/go.sum b/go.sum
deleted file mode 100644
index cdf3767..0000000
--- a/go.sum
+++ /dev/null
@@ -1,68 +0,0 @@
-github.com/briandowns/spinner v1.23.2 h1:Zc6ecUnI+YzLmJniCfDNaMbW0Wid1d5+qcTq4L2FW8w=
-github.com/briandowns/spinner v1.23.2/go.mod h1:LaZeM4wm2Ywy6vO571mvhQNRcWfRUnXOs0RcKV0wYKM=
-github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
-github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
-github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
-github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY=
-github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
-github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
-github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
-github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
-github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
-github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
-github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
-github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
-github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
-github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
-github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
-github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
-github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
-github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
-github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
-github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
-github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
-github.com/rosedblabs/rosedb/v2 v2.4.0 h1:gwI3zHm5tJlvCb6DMtHEhHsq/BiWS2vNS7Lg409ii6k=
-github.com/rosedblabs/rosedb/v2 v2.4.0/go.mod h1:yehP2SiNojwdleiYhvjQU9jWPuXhO7hbWBzohueVPVY=
-github.com/rosedblabs/wal v1.3.8 h1:tErpD9JT/ICiyV3mv5l7qUH6lybn5XF1TbI0e8kvH8M=
-github.com/rosedblabs/wal v1.3.8/go.mod h1:DFvhrmTTeiXvn2btXXT2MW9Nvu99PU0g/pKGgh0+T+o=
-github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/schollz/progressbar/v3 v3.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA=
-github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec=
-github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
-github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
-github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
-github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
-github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
-github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
-github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
-golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
-golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
-golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
-golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/src/flac.rs b/src/flac.rs
new file mode 100644
index 0000000..8b03664
--- /dev/null
+++ b/src/flac.rs
@@ -0,0 +1,130 @@
+use anyhow::{Result, anyhow};
+use flac_bound::{FlacEncoder, WriteWrapper};
+use std::fs::File;
+use symphonia::core::{
+ audio::SampleBuffer,
+ codecs::{DecoderOptions, CODEC_TYPE_NULL},
+ errors::Error::DecodeError,
+ formats::{FormatOptions, Track},
+ io::MediaSourceStream,
+ meta::MetadataOptions,
+ probe::{Hint, ProbeResult},
+};
+
+#[derive(Debug)]
+struct StreamConfig {
+ channels: u32,
+ bits_per_sample: Bps,
+ sample_rate: u32,
+ total_samples_estimate: u64,
+}
+
+#[derive(Debug)]
+enum Bps {
+ _16,
+ _24,
+ _32
+}
+
+fn get_probe(file: &std::path::Path) -> Result<ProbeResult> {
+ let src = std::fs::File::open(file)?;
+ let mss = MediaSourceStream::new(Box::new(src), Default::default());
+ let mut hint = Hint::new();
+ hint.with_extension("flac");
+ let meta_opts: MetadataOptions = Default::default();
+ let fmt_opts: FormatOptions = Default::default();
+ Ok(symphonia::default::get_probe().format(&hint, mss, &fmt_opts, &meta_opts)?)
+}
+
+fn read_streaminfo(track: &Track) -> Result<StreamConfig> {
+ let params = &track.codec_params;
+
+ Ok(StreamConfig {
+ channels: params.channels.unwrap().count() as u32,
+ bits_per_sample: match params.bits_per_sample.unwrap() {
+ 16 => Bps::_16,
+ 24 => Bps::_24,
+ 32 => Bps::_32,
+ _ => return Err(anyhow!("invalid Bps"))
+ },
+ sample_rate: params.sample_rate.unwrap(),
+ total_samples_estimate: params.n_frames.unwrap(),
+ })
+}
+
+pub fn encode_file(file: &std::path::Path) -> Result<()> {
+ let probed = get_probe(file)?;
+ let mut format = probed.format;
+ let dec_opts: DecoderOptions = Default::default();
+ let track = format
+ .tracks()
+ .iter()
+ .find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
+ .unwrap();
+
+ let config = read_streaminfo(track)?;
+
+ let mut outf = File::create(format!("{}.{}", file.display(), "tmp"))?;
+ let mut outw = WriteWrapper(&mut outf);
+ let mut enc = FlacEncoder::new()
+ .unwrap()
+ .channels(config.channels)
+ .bits_per_sample(match config.bits_per_sample {
+ Bps::_16 => 16,
+ Bps::_24 => 24,
+ Bps::_32 => 32
+ })
+ .sample_rate(config.sample_rate)
+ .total_samples_estimate(config.total_samples_estimate)
+ .compression_level(8)
+ .verify(true)
+ .init_write(&mut outw)
+ .unwrap();
+
+ let mut decoder = symphonia::default::get_codecs()
+ .make(&track.codec_params, &dec_opts)
+ .unwrap();
+ let track_id = track.id;
+
+ let mut sample_buf = None;
+
+ loop {
+ let packet = format.next_packet().unwrap();
+
+ if packet.track_id() != track_id {
+ continue;
+ }
+
+ match decoder.decode(&packet) {
+ Ok(audio_buf) => {
+ if sample_buf.is_none() {
+ let spec = *audio_buf.spec();
+
+ let duration = audio_buf.capacity() as u64;
+ sample_buf = Some(SampleBuffer::<i16>::new(duration, spec));
+
+ /* match config.bits_per_sample {
+ Bps::_16 => sample_buf = Some(SampleBuffer::<i16>::new(duration, spec)),
+ Bps::_24 => sample_buf = Some(SampleBuffer::<i24>::new(duration, spec)),
+ Bps::_32 => sample_buf = Some(SampleBuffer::<i32>::new(duration, spec)),
+ } */
+ }
+
+ if let Some(buf) = &mut sample_buf {
+ buf.copy_interleaved_ref(audio_buf);
+ let mut samples = Vec::new();
+ _ = buf.samples().iter().map(|sample| samples.push(*sample as i32)).collect::<Vec<_>>();
+ enc.process_interleaved(samples.as_slice(), samples.iter().len() as u32 / config.channels)
+ .unwrap();
+ }
+ }
+ Err(DecodeError(_)) => (),
+ Err(_) => break,
+ }
+ }
+
+ match enc.finish() {
+ Ok(_) => Ok(()),
+ Err(enc) => Err(anyhow!("Encoding failed:\t{:?}", enc.state())),
+ }
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..6c4cb43
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,5 @@
+mod flac;
+
+fn main() {
+ flac::encode_file(std::path::Path::new("./1.flac")).unwrap();
+}