diff options
| -rw-r--r-- | .gitignore | 7 | ||||
| -rw-r--r-- | Cargo.lock | 319 | ||||
| -rw-r--r-- | Cargo.toml | 11 | ||||
| -rw-r--r-- | cmd/cmd.go | 82 | ||||
| -rw-r--r-- | cmd/init.go | 84 | ||||
| -rw-r--r-- | cmd/main.go | 5 | ||||
| -rw-r--r-- | files/database.go | 172 | ||||
| -rw-r--r-- | files/operations.go | 182 | ||||
| -rw-r--r-- | files/types.go | 17 | ||||
| -rw-r--r-- | go.mod | 34 | ||||
| -rw-r--r-- | go.sum | 68 | ||||
| -rw-r--r-- | src/flac.rs | 130 | ||||
| -rw-r--r-- | src/main.rs | 5 |
13 files changed, 468 insertions, 648 deletions
@@ -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"` -} @@ -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 -) @@ -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(); +} |
