Merge remote-tracking branch 'awalol/master'
This commit is contained in:
commit
9b4e3b87ef
@ -80,6 +80,35 @@ func readKeyFromMMKV(file string, logger *zap.Logger) ([]byte, error) {
|
|||||||
return deriveKey(buf)
|
return deriveKey(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func OpenMMKV(vaultPath string, vaultKey string, logger *zap.Logger) error {
|
||||||
|
filePath, fileName := filepath.Split(vaultPath)
|
||||||
|
mgr, err := mmkv.NewManager(filepath.Dir(filePath))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("init mmkv manager: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
streamKeyVault, err = mgr.OpenVaultCrypto(fileName, vaultKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("open mmkv vault: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug("mmkv vault opened", zap.Strings("keys", streamKeyVault.Keys()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readKeyFromMMKVCustom(mid string) ([]byte, error) {
|
||||||
|
if streamKeyVault == nil {
|
||||||
|
return nil, fmt.Errorf("mmkv vault not loaded")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get ekey from mmkv vault
|
||||||
|
eKey, err := streamKeyVault.GetBytes(mid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("get eKey error: %w", err)
|
||||||
|
}
|
||||||
|
return deriveKey(eKey)
|
||||||
|
}
|
||||||
|
|
||||||
func getRelativeMMKVDir(file string) (string, error) {
|
func getRelativeMMKVDir(file string) (string, error) {
|
||||||
mmkvDir := filepath.Join(filepath.Dir(file), "../mmkv")
|
mmkvDir := filepath.Join(filepath.Dir(file), "../mmkv")
|
||||||
if _, err := os.Stat(mmkvDir); err != nil {
|
if _, err := os.Stat(mmkvDir); err != nil {
|
||||||
|
@ -5,13 +5,12 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"go.uber.org/zap"
|
||||||
"io"
|
"io"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
|
||||||
|
|
||||||
"unlock-music.dev/cli/algo/common"
|
"unlock-music.dev/cli/algo/common"
|
||||||
"unlock-music.dev/cli/internal/sniff"
|
"unlock-music.dev/cli/internal/sniff"
|
||||||
)
|
)
|
||||||
@ -145,6 +144,18 @@ func (d *Decoder) searchKey() (err error) {
|
|||||||
return d.readRawMetaQTag()
|
return d.readRawMetaQTag()
|
||||||
case "STag":
|
case "STag":
|
||||||
return errors.New("qmc: file with 'STag' suffix doesn't contains media key")
|
return errors.New("qmc: file with 'STag' suffix doesn't contains media key")
|
||||||
|
case "cex\x00":
|
||||||
|
footer := qqMusicTagMusicEx{}
|
||||||
|
audioLen, err := footer.Read(d.raw)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.audioLen = int(audioLen)
|
||||||
|
d.decodedKey, err = readKeyFromMMKVCustom(footer.mediafile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
default:
|
default:
|
||||||
size := binary.LittleEndian.Uint32(suffixBuf)
|
size := binary.LittleEndian.Uint32(suffixBuf)
|
||||||
|
|
||||||
|
65
algo/qmc/qmc_footer_musicex.go
Normal file
65
algo/qmc/qmc_footer_musicex.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package qmc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type qqMusicTagMusicEx struct {
|
||||||
|
songid uint32 // Song ID
|
||||||
|
unknown_1 uint32 // unused & unknown
|
||||||
|
unknown_2 uint32 // unused & unknown
|
||||||
|
mid string // Media ID
|
||||||
|
mediafile string // real file name
|
||||||
|
unknown_3 uint32 // unused; uninitialized memory?
|
||||||
|
sizeof_struct uint32 // 19.57: fixed value: 0xC0
|
||||||
|
version uint32 // 19.57: fixed value: 0x01
|
||||||
|
tag_magic []byte // fixed value "musicex\0" (8 bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tag *qqMusicTagMusicEx) Read(raw io.ReadSeeker) (int64, error) {
|
||||||
|
_, err := raw.Seek(-16, io.SeekEnd)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("musicex seek error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
footerBuf := make([]byte, 4)
|
||||||
|
footerBuf, err = io.ReadAll(io.LimitReader(raw, 4))
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("get musicex error: %w", err)
|
||||||
|
}
|
||||||
|
footerLen := int64(binary.LittleEndian.Uint32(footerBuf))
|
||||||
|
|
||||||
|
audioLen, err := raw.Seek(-footerLen, io.SeekEnd)
|
||||||
|
buf, err := io.ReadAll(io.LimitReader(raw, audioLen))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tag.songid = binary.LittleEndian.Uint32(buf[0:4])
|
||||||
|
tag.unknown_1 = binary.LittleEndian.Uint32(buf[4:8])
|
||||||
|
tag.unknown_2 = binary.LittleEndian.Uint32(buf[8:12])
|
||||||
|
|
||||||
|
for i := 0; i < 30; i++ {
|
||||||
|
u := binary.LittleEndian.Uint16(buf[12+i*2 : 12+(i+1)*2])
|
||||||
|
if u == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tag.mid += string(u)
|
||||||
|
}
|
||||||
|
for i := 0; i < 50; i++ {
|
||||||
|
u := binary.LittleEndian.Uint16(buf[72+i*2 : 72+(i+1)*2])
|
||||||
|
if u == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tag.mediafile += string(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
tag.unknown_3 = binary.LittleEndian.Uint32(buf[173:177])
|
||||||
|
tag.sizeof_struct = binary.LittleEndian.Uint32(buf[177:181])
|
||||||
|
tag.version = binary.LittleEndian.Uint32(buf[181:185])
|
||||||
|
tag.tag_magic = buf[185:193]
|
||||||
|
|
||||||
|
return audioLen, nil
|
||||||
|
}
|
@ -5,6 +5,9 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
"go.uber.org/zap"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@ -15,15 +18,11 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
|
|
||||||
"unlock-music.dev/cli/algo/common"
|
"unlock-music.dev/cli/algo/common"
|
||||||
_ "unlock-music.dev/cli/algo/kgm"
|
_ "unlock-music.dev/cli/algo/kgm"
|
||||||
_ "unlock-music.dev/cli/algo/kwm"
|
_ "unlock-music.dev/cli/algo/kwm"
|
||||||
_ "unlock-music.dev/cli/algo/ncm"
|
_ "unlock-music.dev/cli/algo/ncm"
|
||||||
_ "unlock-music.dev/cli/algo/qmc"
|
"unlock-music.dev/cli/algo/qmc"
|
||||||
_ "unlock-music.dev/cli/algo/tm"
|
_ "unlock-music.dev/cli/algo/tm"
|
||||||
_ "unlock-music.dev/cli/algo/xiami"
|
_ "unlock-music.dev/cli/algo/xiami"
|
||||||
_ "unlock-music.dev/cli/algo/ximalaya"
|
_ "unlock-music.dev/cli/algo/ximalaya"
|
||||||
@ -50,6 +49,8 @@ func main() {
|
|||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{Name: "input", Aliases: []string{"i"}, Usage: "path to input file or dir", Required: false},
|
&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},
|
&cli.StringFlag{Name: "output", Aliases: []string{"o"}, Usage: "path to output dir", Required: false},
|
||||||
|
&cli.StringFlag{Name: "vault-file", Aliases: []string{"db"}, Usage: "数据库文件位置 (请确保crc文件在同目录下)", Required: false},
|
||||||
|
&cli.StringFlag{Name: "vault-key", Aliases: []string{"key"}, Usage: "数据库密钥 (length 32)", Required: false},
|
||||||
&cli.BoolFlag{Name: "remove-source", Aliases: []string{"rs"}, Usage: "remove source file", Required: false, Value: false},
|
&cli.BoolFlag{Name: "remove-source", Aliases: []string{"rs"}, Usage: "remove source file", Required: false, Value: false},
|
||||||
&cli.BoolFlag{Name: "skip-noop", Aliases: []string{"n"}, Usage: "skip noop decoder", Required: false, Value: true},
|
&cli.BoolFlag{Name: "skip-noop", Aliases: []string{"n"}, Usage: "skip noop decoder", Required: false, Value: true},
|
||||||
&cli.BoolFlag{Name: "update-metadata", Usage: "update metadata & album art from network", Required: false, Value: false},
|
&cli.BoolFlag{Name: "update-metadata", Usage: "update metadata & album art from network", Required: false, Value: false},
|
||||||
@ -130,6 +131,15 @@ func appMain(c *cli.Context) (err error) {
|
|||||||
return errors.New("output should be a writable directory")
|
return errors.New("output should be a writable directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vaultPath := c.String("vault-file")
|
||||||
|
vaultKey := c.String("vault-key")
|
||||||
|
if vaultPath != "" && vaultKey != "" {
|
||||||
|
err := qmc.OpenMMKV(vaultPath, vaultKey, logger)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
proc := &processor{
|
proc := &processor{
|
||||||
outputDir: output,
|
outputDir: output,
|
||||||
skipNoopDecoder: c.Bool("skip-noop"),
|
skipNoopDecoder: c.Bool("skip-noop"),
|
||||||
|
Loading…
Reference in New Issue
Block a user