diff --git a/cmd/um/main.go b/cmd/um/main.go new file mode 100644 index 0000000..ff0ebff --- /dev/null +++ b/cmd/um/main.go @@ -0,0 +1,134 @@ +package main + +import ( + "errors" + "github.com/umlock-music/cli/algo/common" + _ "github.com/umlock-music/cli/algo/kgm" + _ "github.com/umlock-music/cli/algo/kwm" + _ "github.com/umlock-music/cli/algo/ncm" + _ "github.com/umlock-music/cli/algo/qmc" + _ "github.com/umlock-music/cli/algo/tm" + _ "github.com/umlock-music/cli/algo/xm" + "github.com/umlock-music/cli/internal/logging" + "github.com/urfave/cli/v2" + "go.uber.org/zap" + "log" + "os" + "path/filepath" + "strings" +) + +func main() { + app := cli.App{ + Name: "Unlock Music CLI", + HelpName: "um", + Usage: "Unlock your encrypted music file https://github.com/unlock-music/cli", + Version: "v0.0.1", + Flags: []cli.Flag{ + &cli.StringFlag{Name: "input", Aliases: []string{"i"}, Usage: "path to input file or dir", Required: true}, + &cli.StringFlag{Name: "output", Aliases: []string{"o"}, Usage: "path to output dir", Required: true}, + }, + Action: appMain, + Copyright: "Copyright (c) 2020 Unlock Music https://github.com/unlock-music/cli/blob/master/LICENSE", + HideHelpCommand: true, + UsageText: "um -i /path/to/input -o /path/to/output/dir", + } + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } +} + +func appMain(c *cli.Context) error { + input := c.String("input") + output := c.String("output") + inputStat, err := os.Stat(input) + if err != nil { + return err + } + + outputStat, err := os.Stat(output) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + err = os.MkdirAll(output, 0755) + } + if err != nil { + return err + } + } else if !outputStat.IsDir() { + return errors.New("output should be a writable directory") + } + + if inputStat.IsDir() { + return dealDirectory(input, output) + } else { + ext := strings.TrimLeft(filepath.Ext(inputStat.Name()), ".") + allDec := common.GetDecoder(ext) + if len(allDec) == 0 { + logging.Log().Fatal("skipping while no suitable decoder") + } + return tryDecFile(input, output, allDec) + } + +} +func dealDirectory(inputDir string, outputDir string) error { + items, err := os.ReadDir(inputDir) + if err != nil { + return err + } + for _, item := range items { + if item.IsDir() { + continue + } + ext := strings.TrimLeft(filepath.Ext(item.Name()), ".") + allDec := common.GetDecoder(ext) + if len(allDec) == 0 { + logging.Log().Info("skipping while no suitable decoder", zap.String("file", item.Name())) + continue + } + + err := tryDecFile(filepath.Join(inputDir, item.Name()), outputDir, allDec) + if err != nil { + logging.Log().Error("conversion failed", zap.String("source", item.Name())) + } + } + return nil +} + +func tryDecFile(inputFile string, outputDir string, allDec []common.NewDecoderFunc) error { + file, err := os.ReadFile(inputFile) + if err != nil { + return err + } + + var dec common.Decoder + for _, decFunc := range allDec { + dec = decFunc(file) + if err := dec.Validate(); err == nil { + break + } + logging.Log().Warn("try decode failed", zap.Error(err)) + dec = nil + } + if dec == nil { + return errors.New("no any decoder can resolve the file") + } + if err := dec.Decode(); err != nil { + return errors.New("failed while decoding: " + err.Error()) + } + + outExt := dec.GetAudioExt() + if outExt == "" { + outExt = "mp3" + } + filenameOnly := strings.TrimSuffix(filepath.Base(inputFile), filepath.Ext(inputFile)) + + outPath := filepath.Join(outputDir, filenameOnly+"."+outExt) + err = os.WriteFile(outPath, dec.GetAudioData(), 0644) + if err != nil { + return err + } + logging.Log().Info("successfully converted", + zap.String("source", inputFile), zap.String("destination", outPath)) + return nil +} diff --git a/go.mod b/go.mod index 657ffd1..b3c1d3a 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,11 @@ module github.com/umlock-music/cli go 1.16 require ( + github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/davecgh/go-spew v1.1.1 + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ulikunitz/xz v0.5.9 + github.com/urfave/cli/v2 v2.3.0 go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.16.0 ) diff --git a/go.sum b/go.sum index c6b8cfa..3801a5f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,9 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= @@ -13,12 +17,20 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE 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/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +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/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -51,5 +63,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=