5 Commits

Author SHA1 Message Date
15e340eac4 Update RawDecoder & Support .kwm as .aac 2021-05-23 19:59:29 +08:00
c836ac7cb5 Clean TODOs 2021-05-23 19:58:23 +08:00
ad301e0ff2 Update Help Text 2021-05-16 17:21:20 +08:00
1f0aefb72d Update README.md 2021-05-16 17:18:37 +08:00
c71ad9cc79 Close #10 Drag to Decrypt 2021-05-16 17:15:52 +08:00
9 changed files with 61 additions and 33 deletions

View File

@ -1,18 +1,25 @@
# Unlock Music Project - CLI Edition
Original: Web Edition https://github.com/ix64/unlock-music
- [Release Download](https://github.com/unlock-music/cli/releases/latest)
## Features
- [x] All Algorithm Supported By `ix64/unlock-music`
- [ ] Complete Cover Image
- [ ] Parse Meta Data
- [ ] Complete Meta Data
## Hou to Build
- Requirements: **Golang 1.16**
1. Clone this repo `git clone https://github.com/unlock-music/cli && cd cli`
2. Build the executable `go build ./cmd/um`
2. Build the executable `go build ./cmd/um`
## How to use
- Drag the encrypted file to `um.exe` (Tested on Windows)
- Run: `./um [-o <output dir>] [-i] <input dir/file>`
- Use `./um -h` to show help menu

View File

@ -1,17 +1,24 @@
package common
import "errors"
type RawDecoder struct {
file []byte
audioExt string
}
//goland:noinspection GoUnusedExportedFunction
func NewRawDecoder(file []byte) Decoder {
return &RawDecoder{file: file}
}
func (d RawDecoder) Validate() error {
return nil
func (d *RawDecoder) Validate() error {
for ext, sniffer := range snifferRegistry {
if sniffer(d.file) {
d.audioExt = ext
return nil
}
}
return errors.New("audio doesn't recognized")
}
func (d RawDecoder) Decode() error {
@ -27,21 +34,19 @@ func (d RawDecoder) GetAudioData() []byte {
}
func (d RawDecoder) GetAudioExt() string {
return "." + d.audioExt
return d.audioExt
}
func (d RawDecoder) GetMeta() Meta {
return nil
}
func DecoderFuncWithExt(ext string) NewDecoderFunc {
return func(file []byte) Decoder {
return &RawDecoder{file: file, audioExt: ext}
}
}
func init() {
/*RegisterDecoder("mp3", DecoderFuncWithExt("mp3"))
RegisterDecoder("flac", DecoderFuncWithExt("flac"))
RegisterDecoder("wav", DecoderFuncWithExt("wav"))
RegisterDecoder("ogg", DecoderFuncWithExt("ogg"))
RegisterDecoder("m4a", DecoderFuncWithExt("m4a"))*/
RegisterDecoder("mp3", NewRawDecoder)
RegisterDecoder("flac", NewRawDecoder)
RegisterDecoder("ogg", NewRawDecoder)
RegisterDecoder("m4a", NewRawDecoder)
RegisterDecoder("wav", NewRawDecoder)
RegisterDecoder("wma", NewRawDecoder)
RegisterDecoder("aac", NewRawDecoder)
}

View File

@ -5,12 +5,13 @@ import "bytes"
type Sniffer func(header []byte) bool
var snifferRegistry = map[string]Sniffer{
".m4a": SnifferM4A,
".ogg": SnifferOGG,
".mp3": SnifferMP3,
".flac": SnifferFLAC,
".ogg": SnifferOGG,
".m4a": SnifferM4A,
".wav": SnifferWAV,
".wma": SnifferWMA,
".mp3": SnifferMP3,
".aac": SnifferAAC,
}
func SniffAll(header []byte) (string, bool) {
@ -42,3 +43,6 @@ func SnifferWAV(header []byte) bool {
func SnifferWMA(header []byte) bool {
return bytes.HasPrefix(header, []byte("\x30\x26\xb2\x75\x8e\x66\xcf\x11\xa6\xd9\x00\xaa\x00\x62\xce\x6c"))
}
func SnifferAAC(header []byte) bool {
return bytes.HasPrefix(header, []byte{0xFF, 0xF1})
}

View File

@ -40,7 +40,7 @@ func (d Decoder) GetAudioData() []byte {
}
func (d Decoder) GetAudioExt() string {
return ""
return "" // use sniffer
}
func (d Decoder) GetMeta() common.Meta {
@ -58,9 +58,8 @@ func (d *Decoder) Validate() error {
d.key = d.file[0x1c:0x2c]
d.key = append(d.key, 0x00)
_ = d.file[0x2c:0x3c] //key2
_ = d.file[0x2c:0x3c] //todo: key2
return nil
}
func (d *Decoder) Decode() error {
@ -69,7 +68,8 @@ func (d *Decoder) Decode() error {
lenData := len(dataEncrypted)
initMask()
if fullMaskLen < lenData {
logging.Log().Warn("文件过大,处理后的音频不完整,请向我们报告此文件的信息")
logging.Log().Warn("The file is too large and the processed audio is incomplete, " +
"please report to us about this file at https://github.com/unlock-music/cli/issues")
lenData = fullMaskLen
}
d.audio = make([]byte, lenData)

View File

@ -41,7 +41,7 @@ var maskV2 []byte
var fullMaskLen int
var initMaskOK = false
//todo: 根据需求解压Mask大小
//todo: decompress mask on demand
func initMask() {
if initMaskOK {
return

View File

@ -125,4 +125,5 @@ func padOrTruncate(raw string, length int) string {
func init() {
// Kuwo Mp3/Flac
common.RegisterDecoder("kwm", NewDecoder)
common.RegisterDecoder("kwm", common.NewRawDecoder)
}

View File

@ -61,7 +61,6 @@ func (d *Decoder) Validate() error {
return nil
}
//todo: 读取前进行检查长度,防止越界
func (d *Decoder) readKeyData() error {
if d.offsetKey == 0 || d.offsetKey+4 > d.fileLen {
return errors.New("invalid cover file offset")

View File

@ -73,7 +73,7 @@ func init() {
common.RegisterDecoder("tm2", DecoderFuncWithExt("m4a"))
common.RegisterDecoder("tm6", DecoderFuncWithExt("m4a"))
// QQ Music IOS Mp3
common.RegisterDecoder("tm0", common.DecoderFuncWithExt("mp3"))
common.RegisterDecoder("tm3", common.DecoderFuncWithExt("mp3"))
common.RegisterDecoder("tm0", common.NewRawDecoder)
common.RegisterDecoder("tm3", common.NewRawDecoder)
}

View File

@ -12,13 +12,12 @@ import (
"github.com/unlock-music/cli/internal/logging"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"log"
"os"
"path/filepath"
"strings"
)
var AppVersion = "0.0.3"
var AppVersion = "0.0.4"
func main() {
app := cli.App{
@ -27,23 +26,36 @@ func main() {
Usage: "Unlock your encrypted music file https://github.com/unlock-music/cli",
Version: AppVersion,
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},
&cli.StringFlag{Name: "input", Aliases: []string{"i"}, Usage: "path to input file or dir", Required: false},
&cli.StringFlag{Name: "output", Aliases: []string{"o"}, Usage: "path to output dir", Required: false},
},
Action: appMain,
Copyright: "Copyright (c) 2020 Unlock Music https://github.com/unlock-music/cli/blob/master/LICENSE",
Copyright: "Copyright (c) 2020 - 2021 Unlock Music https://github.com/unlock-music/cli/blob/master/LICENSE",
HideHelpCommand: true,
UsageText: "um -i /path/to/input -o /path/to/output/dir",
UsageText: "um [-o /path/to/output/dir] [-i] /path/to/input",
}
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
logging.Log().Fatal("run app failed", zap.Error(err))
}
}
func appMain(c *cli.Context) error {
input := c.String("input")
if input == "" && c.Args().Present() {
input = c.Args().Get(c.Args().Len() - 1)
}
output := c.String("output")
if output == "" {
var err error
output, err = os.Getwd()
if err != nil {
return err
}
}
inputStat, err := os.Stat(input)
if err != nil {
return err