From 2200ba4b034ce32ad8259a0bbd1cbb56d19ffc57 Mon Sep 17 00:00:00 2001 From: anki Date: Thu, 3 Oct 2019 20:12:57 +0800 Subject: [PATCH] first commit --- .gitignore | 3 + README.md | Bin 0 -> 204 bytes download.go | 41 ++++++++++ kugou/api.go | 100 ++++++++++++++++++++++++ kugou/api_test.go | 21 +++++ main.go | 91 ++++++++++++++++++++++ makefile | 61 +++++++++++++++ netease/algorithm.go | 153 +++++++++++++++++++++++++++++++++++++ netease/api.go | 178 +++++++++++++++++++++++++++++++++++++++++++ netease/api_test.go | 81 ++++++++++++++++++++ netease/myCrypto.go | 46 +++++++++++ songBean/bean.go | 22 ++++++ wait.md | 10 +++ 13 files changed, 807 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 download.go create mode 100644 kugou/api.go create mode 100644 kugou/api_test.go create mode 100644 main.go create mode 100644 makefile create mode 100644 netease/algorithm.go create mode 100644 netease/api.go create mode 100644 netease/api_test.go create mode 100644 netease/myCrypto.go create mode 100644 songBean/bean.go create mode 100644 wait.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b610624 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vscode/* +*.exe +bin/* diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..80438fb88ef31f775a9f9771f2ecbf3e6324a1f7 GIT binary patch literal 204 zcmezWPnki1A)O(g!Izmnqc<=0Hp#Y$N&HU literal 0 HcmV?d00001 diff --git a/download.go b/download.go new file mode 100644 index 0000000..9390439 --- /dev/null +++ b/download.go @@ -0,0 +1,41 @@ +package main + +import ( + "log" + "net/http" + "os" + "strings" +) + +func Download(url, name, path string) error { + path = strings.TrimSpace(path) + if len(path) == 0 { + path = "./" + } else if path[len(path)-1] != '/' { + path = path + "/" + } + tmp := strings.Split(url, ".") + ext := tmp[len(tmp)-1] + name = name + "." + ext + rs, err := http.Get(url) + if err != nil { + log.Println("download fail:", err.Error()) + return err + } + file, err := os.Create(path + name) + if err != nil { + log.Println("create file fail:", err.Error()) + return err + } + buf := make([]byte, 262144) + for { + len, err := rs.Body.Read(buf) + file.Write(buf[:len]) + if err != nil { + break + } + } + file.Close() + rs.Body.Close() + return nil +} diff --git a/kugou/api.go b/kugou/api.go new file mode 100644 index 0000000..21745a0 --- /dev/null +++ b/kugou/api.go @@ -0,0 +1,100 @@ +package kugou + +import ( + "crypto/md5" + "encoding/hex" + "encoding/json" + "log" + "net/http" + + "github.com/ankikong/goMusic/songBean" +) + +type kugouSongUrl struct { + SongBr int `json:"bitRate"` + SongSize int `json:"fileSize"` + Urls []string `json:"url"` + SongName string `json:"fileName"` +} + +type kugouSearchPerResult struct { + HQHash string `json:"HQFileHash"` + HQSize int `json:"HQFileSize"` + SQHash string `json:"SQFileHash"` + SQSize int `json:"SQFileSize"` + FileName string `json:"FileName"` + ArtistName string `json:"SingerName"` + AlbumName string `json:"AlbumName"` +} + +func (kg kugouSearchPerResult) GetFileName() string { + return kg.FileName +} + +func (kg kugouSearchPerResult) GetArtistName() string { + return kg.ArtistName +} + +func (kg kugouSearchPerResult) GetAlbumName() string { + return kg.AlbumName +} + +func (kg kugouSearchPerResult) GetUrl(br int) songBean.SongInfo { + if br == 990 && kg.SQHash != "" { + return GetSongUrl([]string{kg.SQHash})[0] + } else { + return GetSongUrl([]string{kg.HQHash})[0] + } +} + +type kugouData struct { + List []kugouSearchPerResult `json:"lists"` +} +type kugouSearchResult struct { + Data kugouData +} + +func MD5(text string) string { + data := []byte(text) + hash := md5.Sum(data) + return hex.EncodeToString(hash[:]) +} + +func doGet(url string) []byte { + rs, err := http.Get(url) + if err != nil { + log.Println(err) + return nil + } + defer rs.Body.Close() + tmpBuf := make([]byte, 65536) + len, _ := rs.Body.Read(tmpBuf) + return tmpBuf[:len] +} + +func GetSongUrl(ids []string) []songBean.SongInfo { + ansRet := make([]songBean.SongInfo, len(ids)) + index := 0 + for _, id := range ids { + tmpHash := MD5(id + "kgcloudv2") + api := `http://trackercdn.kugou.com/i/v2/?key=` + tmpHash + `&hash=` + id + `&br=hq&appid=1005&pid=2&cmd=25&behavior=play` + tmpBuf := doGet(api) + var song kugouSongUrl + json.Unmarshal(tmpBuf, &song) + ansRet[index].SongBr = song.SongBr + ansRet[index].SongName = song.SongName + ansRet[index].SongSize = song.SongSize + ansRet[index].SongUrl = song.Urls[0] + index++ + } + return ansRet +} + +func Search(word string) []kugouSearchPerResult { + Url := `http://songsearch.kugou.com/song_search_v2?pagesize=5&keyword=` + word + rs := doGet(Url) + // fmt.Println(string(rs)) + var ans kugouSearchResult + json.Unmarshal(rs, &ans) + return ans.Data.List +} diff --git a/kugou/api_test.go b/kugou/api_test.go new file mode 100644 index 0000000..d61e569 --- /dev/null +++ b/kugou/api_test.go @@ -0,0 +1,21 @@ +package kugou + +import ( + "fmt" + "testing" + + "github.com/ankikong/goMusic/songBean" +) + +func TestGetSongUrl(t *testing.T) { + rs := GetSongUrl([]string{"4D870D0DEB13AA6700BEECA513C6B03C", "40AD169093CDE5523A13DA8E7A09066B"}) + fmt.Println(rs) +} + +func TestSearch(t *testing.T) { + rs := Search("claris") + fmt.Println(rs) + rs[0].GetUrl(320) + var val songBean.SongUrl = rs[0] + fmt.Println(val.GetUrl(320)) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..e682528 --- /dev/null +++ b/main.go @@ -0,0 +1,91 @@ +package main + +import ( + "flag" + "fmt" + "os" + "regexp" + "strconv" + "strings" + + "github.com/ankikong/goMusic/kugou" + "github.com/ankikong/goMusic/songBean" + + "github.com/ankikong/goMusic/netease" + "github.com/jedib0t/go-pretty/table" +) + +func search(text string) { + var result []songBean.SongData + for _, rs := range netease.Search(text) { + result = append(result, rs) + } + for _, rs := range kugou.Search(text) { + result = append(result, rs) + } + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.AppendHeader(table.Row{"编号", "歌名", "歌手", "专辑"}) + // fmt.Println("歌名\t歌手\t专辑") + // fmt.Printf("%-26s%-26s%-26s\n", "歌名", "歌手", "专辑") + index := 0 + for _, rs := range result { + t.AppendRow(table.Row{index, rs.GetFileName(), rs.GetArtistName(), rs.GetAlbumName()}) + index++ + // fmt.Printf("%-26s%-26s%-26s\n", rs.GetFileName(), rs.GetArtistName(), rs.GetAlbumName()) + } + var input string + var num uint64 + // stdin := bufio.NewReader(os.Stdin) + t.Render() + for { + fmt.Print("please input integer(input q to quit):") + // fmt.Println(input) + fmt.Scan(&input) + // fmt.Fscan(stdin, input) + input = strings.TrimSpace(input) + input = strings.ToLower(input) + if input == "q" { + return + } + nums, err := strconv.ParseUint(input, 10, 20) + if err != nil { + continue + } + num = nums + break + } + rss := result[num].GetUrl(320) + fmt.Println(rss.SongUrl) + Download(rss.SongUrl, rss.SongName, "") +} + +func GetByNeteaseId(url string) { + reg, _ := regexp.Compile(`\Wid=\d+`) + ids := reg.FindAllString(url, -1) + if len(ids) == 0 { + fmt.Println("输入错误") + return + } + id := ids[0][4:] + rs := netease.GetSongUrl([]string{fmt.Sprint(id)}, 320)[0] + fmt.Println("开始下载:", rs.SongName) + Download(rs.SongUrl, rs.SongName, "") +} + +func main() { + var ( + url string + keyword string + ) + flag.StringVar(&url, "url", "", "url of song") + flag.StringVar(&keyword, "kw", "", "search keyword") + flag.Parse() + if len(keyword) > 0 { + search(keyword) + } else { + if strings.Contains(url, "music.163.com") { + GetByNeteaseId(url) + } + } +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..b51a796 --- /dev/null +++ b/makefile @@ -0,0 +1,61 @@ +# Go parameters +GOCMD=go +GOBUILD=$(GOCMD) build +GOCLEAN=$(GOCMD) clean +GOTEST=$(GOCMD) test +GOGET=$(GOCMD) get + +RELEASE_VERSION=V0.0.1 +BINARY_NAME=goMusic-$(RELEASE_VERSION) +BINARY_DIR=bin + + +all: test build +build: + $(GOBUILD) -o $(BINARY_DIR)/$(BINARY_NAME).exe -v +test: + $(GOTEST) -v ./... +clean: + $(GOCLEAN) + rm -f $(BINARY_NAME) + rm -f $(BINARY_UNIX) +run: + $(GOBUILD) -o $(BINARY_NAME) -v ./... + ./$(BINARY_NAME) + +deps: + $(GOGET) github.com/jedib0t/go-pretty/table + +# Cross compilation + +# windows +build-win64: + CGO_ENABLED=0 GOOS=windows GOARCH=amd64 $(GOBUILD) -o $(BINARY_DIR)/$(BINARY_NAME)_win64.exe -v +build-win32: + CGO_ENABLED=0 GOOS=windows GOARCH=386 $(GOBUILD) -o $(BINARY_DIR)/$(BINARY_NAME)_win32.exe -v + +# Linux +build-linux-amd64: + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) -o $(BINARY_DIR)/$(BINARY_NAME)_linux_amd64 -v +build-linux-X86: + CGO_ENABLED=0 GOOS=linux GOARCH=386 $(GOBUILD) -o $(BINARY_DIR)/$(BINARY_NAME)_linux_X86 -v +build-linux-arm64: + CGO_ENABLED=0 GOOS=linux GOARCH=arm64 $(GOBUILD) -o $(BINARY_DIR)/$(BINARY_NAME)_linux_arm64 -v +build-linux-armV7: + CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 $(GOBUILD) -o $(BINARY_DIR)/$(BINARY_NAME)_linux_armV7 -v + +# Mac +build-darwin-X86: + CGO_ENABLED=0 GOOS=darwin GOARCH=386 $(GOBUILD) -o $(BINARY_DIR)/$(BINARY_NAME)_darwin_X86 -v +build-darwin-amd64: + CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 $(GOBUILD) -o $(BINARY_DIR)/$(BINARY_NAME)_darwin_amd64 -v + +build-all: + make build-win64 + make build-win32 + make build-linux-amd64 + make build-linux-X86 + make build-linux-arm64 + make build-linux-armV7 + make build-darwin-X86 + make build-darwin-amd64 diff --git a/netease/algorithm.go b/netease/algorithm.go new file mode 100644 index 0000000..c9d4a57 --- /dev/null +++ b/netease/algorithm.go @@ -0,0 +1,153 @@ +package netease + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/md5" + "crypto/rand" + "encoding/hex" + "io" + "math/big" +) + +// https://blog.csdn.net/mirage003/article/details/87868999 +// =================== CBC ====================== +func AesEncryptCBC(origData, iv, key []byte) (encrypted []byte) { + block, _ := aes.NewCipher(key) + blockSize := block.BlockSize() + origData = pkcs5Padding(origData, blockSize) + blockMode := cipher.NewCBCEncrypter(block, iv) + encrypted = make([]byte, len(origData)) + blockMode.CryptBlocks(encrypted, origData) + return encrypted +} +func AesDecryptCBC(encrypted []byte, key []byte) (decrypted []byte) { + block, _ := aes.NewCipher(key) + blockSize := block.BlockSize() + blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) + decrypted = make([]byte, len(encrypted)) + blockMode.CryptBlocks(decrypted, encrypted) + decrypted = pkcs5UnPadding(decrypted) + return decrypted +} +func pkcs5Padding(ciphertext []byte, blockSize int) []byte { + padding := blockSize - len(ciphertext)%blockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(ciphertext, padtext...) +} +func pkcs5UnPadding(origData []byte) []byte { + length := len(origData) + unpadding := int(origData[length-1]) + return origData[:(length - unpadding)] +} + +// =================== ECB ====================== +func AesEncryptECB(origData []byte, key []byte) (encrypted []byte) { + cipher, _ := aes.NewCipher(generateKey(key)) + length := (len(origData) + aes.BlockSize) / aes.BlockSize + plain := make([]byte, length*aes.BlockSize) + copy(plain, origData) + pad := byte(len(plain) - len(origData)) + for i := len(origData); i < len(plain); i++ { + plain[i] = pad + } + encrypted = make([]byte, len(plain)) + for bs, be := 0, cipher.BlockSize(); bs <= len(origData); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() { + cipher.Encrypt(encrypted[bs:be], plain[bs:be]) + } + + return encrypted +} +func AesDecryptECB(encrypted []byte, key []byte) (decrypted []byte) { + cipher, _ := aes.NewCipher(generateKey(key)) + decrypted = make([]byte, len(encrypted)) + for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() { + cipher.Decrypt(decrypted[bs:be], encrypted[bs:be]) + } + + trim := 0 + if len(decrypted) > 0 { + trim = len(decrypted) - int(decrypted[len(decrypted)-1]) + } + + return decrypted[:trim] +} +func generateKey(key []byte) (genKey []byte) { + genKey = make([]byte, 16) + copy(genKey, key) + for i := 16; i < len(key); { + for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 { + genKey[j] ^= key[i] + } + } + return genKey +} + +// =================== CFB ====================== +func AesEncryptCFB(origData []byte, key []byte) (encrypted []byte) { + block, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + encrypted = make([]byte, aes.BlockSize+len(origData)) + iv := encrypted[:aes.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + panic(err) + } + stream := cipher.NewCFBEncrypter(block, iv) + stream.XORKeyStream(encrypted[aes.BlockSize:], origData) + return encrypted +} +func AesDecryptCFB(encrypted []byte, key []byte) (decrypted []byte) { + block, _ := aes.NewCipher(key) + if len(encrypted) < aes.BlockSize { + panic("ciphertext too short") + } + iv := encrypted[:aes.BlockSize] + encrypted = encrypted[aes.BlockSize:] + + stream := cipher.NewCFBDecrypter(block, iv) + stream.XORKeyStream(encrypted, encrypted) + return encrypted +} + +// rsa start +func Qpow(aa, bb, mod big.Int) big.Int { + two, one, ans, zero, tmp := big.NewInt(2), big.NewInt(1), big.NewInt(1), big.NewInt(0), big.NewInt(0) + for bb.Cmp(zero) != 0 { + if zero.Cmp(tmp.And(&bb, one)) != 0 { + ans.Mul(ans, &aa) + ans.Mod(ans, &mod) + } + bb.Div(&bb, two) + aa.Mul(&aa, &aa) + aa.Mod(&aa, &mod) + } + return *ans +} + +func rsaEncrypt(buf []byte) string { + mod, a := big.NewInt(0), big.NewInt(0) + mod.SetString(modulus, 16) + a.SetBytes(buf) + rs := Qpow(*a, *big.NewInt(65537), *mod) + ans := hex.EncodeToString(rs.Bytes()) + + if len(ans) > 256 { + return ans[len(ans)-256:] + } else { + for len(ans) < 256 { + ans = "0" + ans + } + return ans + } +} + +// md5 start + +func MD5(text string) string { + data := []byte(text) + hash := md5.Sum(data) + return hex.EncodeToString(hash[:]) +} diff --git a/netease/api.go b/netease/api.go new file mode 100644 index 0000000..3f5e729 --- /dev/null +++ b/netease/api.go @@ -0,0 +1,178 @@ +package netease + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "regexp" + "strings" + + "github.com/ankikong/goMusic/songBean" +) + +type NeteaseSong struct { + Id int `json:"id"` + Url string `json:"url"` + Br int `json:"br"` + Size int `json:"size"` + // SongType string `json"type"` +} +type NeteaseSongRes struct { + Data []NeteaseSong `json:"data"` + Code int `json:"code"` +} + +func doHttp(method, Url, data, encryptoMethod string) string { + rs := make(map[string]string) + if encryptoMethod == "web" { + rs, _ = weapi(data) + } else if encryptoMethod == "linux" { + rs = linuxApi(data) + } else { + rs = nil + } + str := new(strings.Builder) + if rs != nil { + for k, v := range rs { + str.WriteString(k + "=" + v + "&") + } + data = str.String() + data = data[:len(data)-1] + } + req, _ := http.NewRequest(method, Url, strings.NewReader(data)) + req.Header.Add("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36") + req.Header.Add("origin", "https://music.163.com") + if rs != nil { + req.Header.Add("content-type", "application/x-www-form-urlencoded") + } + + client := http.Client{} + res, err := client.Do(req) + if err != nil { + log.Println(err.Error()) + return "" + } + defer res.Body.Close() + resString := new(strings.Builder) + tmpBuf := make([]byte, 4096) + for { + leng, err := res.Body.Read(tmpBuf) + resString.Write(tmpBuf[:leng]) + if err != nil { + break + } + } + return resString.String() +} + +// 获取网易的歌曲播放地址 +// ids是歌曲的id,br是码率,取990,320,192,128 +func GetSongUrl(ids []string, br int) []songBean.SongInfo { + input := `{"method":"POST","url":"https://music.163.com/api/song/enhance/player/url","params":{"ids":"[` + + strings.Join(ids, ",") + `]","br":` + fmt.Sprintf("%d", br*1000) + `}}` + rs := doHttp("POST", "https://music.163.com/api/linux/forward", input, "linux") + var ans NeteaseSongRes + json.Unmarshal([]byte(rs), &ans) + ansRet := make([]songBean.SongInfo, len(ids)) + reg, _ := regexp.Compile(`"title": "[^"]+`) + index := 0 + for _, id := range ids { + tmpRs := doHttp("GET", "https://music.163.com/song?id="+id, "", "null") + songname := reg.FindAllString(tmpRs, 1)[0][10:] + ansRet[index].SongBr = ans.Data[index].Br + ansRet[index].SongUrl = ans.Data[index].Url + ansRet[index].SongName = string(songname) + ansRet[index].SongSize = ans.Data[index].Size + index++ + } + return ansRet +} + +type neteaseSearchForm struct { + S string `json:"s"` + Type int `json:"type"` + Limit int `json:"limit"` + Offset int `json:"offset"` + Csrf string `json:"csrf_token"` +} + +type neteaseSearchArist struct { + Name string `json:"name"` +} +type neteaseSearchPerResult struct { + Album struct { + AlbumName string `json:"name"` + } `json:"album"` + Artists []neteaseSearchArist `json:"artists"` + SongName string `json:"name"` + SongId int `json:"id"` +} + +type neteaseSearch struct { + Result struct { + Results []neteaseSearchPerResult `json:"songs"` + } `json:"result"` +} + +type neteaseSearchResult struct { + FileName string + ArtistName string + AlbumName string + SongId int +} + +func (kg neteaseSearchResult) GetFileName() string { + return kg.FileName +} + +func (kg neteaseSearchResult) GetArtistName() string { + return kg.ArtistName +} + +func (kg neteaseSearchResult) GetAlbumName() string { + return kg.AlbumName +} + +func (nt neteaseSearchResult) GetUrl(br int) songBean.SongInfo { + songId := fmt.Sprint(nt.SongId) + if br != 990 && br != 320 && br != 192 && br != 128 { + br = 320 + } + return GetSongUrl([]string{songId}, br)[0] +} + +func Search(text string) []neteaseSearchResult { + query := new(neteaseSearchForm) + query.S = text + query.Type = 1 + query.Csrf = "" + query.Limit = 10 + query.Offset = 0 + rs, _ := json.Marshal(query) + ans := doHttp("POST", "https://music.163.com/weapi/search/get", string(rs), "web") + var tmpAns neteaseSearch + err := json.Unmarshal([]byte(ans), &tmpAns) + if err != nil { + log.Println(err) + } + ansRet := make([]neteaseSearchResult, len(tmpAns.Result.Results)) + index := 0 + for _, result := range tmpAns.Result.Results { + ansRet[index].AlbumName = result.Album.AlbumName + arName := new(strings.Builder) + for _, name := range result.Artists { + arName.WriteString(name.Name) + arName.WriteString("/") + } + tmpName := arName.String() + if len(tmpName) > 1 { + tmpName = tmpName[:len(tmpName)-1] + } + ansRet[index].ArtistName = tmpName + ansRet[index].FileName = result.SongName + ansRet[index].SongId = result.SongId + index++ + } + return ansRet +} diff --git a/netease/api_test.go b/netease/api_test.go new file mode 100644 index 0000000..0a1f9ef --- /dev/null +++ b/netease/api_test.go @@ -0,0 +1,81 @@ +package netease + +import ( + "fmt" + "math/big" + "regexp" + "testing" + + "github.com/ankikong/goMusic/songBean" +) + +func TestRsaEncrypt(t *testing.T) { + rs := rsaEncrypt([]byte("aaabaaabaaabaaab")) + fmt.Println("rs=" + rs) + a := big.NewInt(0) + a.SetString("010001", 16) + fmt.Printf("%v", a) +} + +func TestWeapi(t *testing.T) { + // http://music.163.com/weapi/song/enhance/player/url?csrf_token= + testInput := `{"ids":["28528420","347230"],"br":320000,"csrf_token":""}` + param, err := weapi(testInput) + if err != nil { + t.Error(err) + } else { + fmt.Println(param) + } +} + +func TestLinuxApi(t *testing.T) { + // testInput := `{"ids":"[33911781]","br":999000}` + // https://music.163.com/api/linux/forward + testInput := `{"method":"POST","url":"https://music.163.com/api/song/enhance/player/url","params":{"ids":"[28445807,474567613]","br":999000}}` + // param := linuxApi(testInput) + // fmt.Println(param) + fmt.Println(testInput) + fmt.Println(linuxApi(testInput)) +} + +func TestQpow(t *testing.T) { + rs := Qpow(*big.NewInt(111111111), *big.NewInt(111111111), *big.NewInt(int64(1e9 + 7))) + fmt.Printf("%v\n", &rs) +} + +func TestRaEncrypt(t *testing.T) { + rsaEncrypt([]byte("10000000000000")) +} + +func TestMD5(t *testing.T) { + fmt.Println(MD5("a")) +} + +func TestDoHttp(t *testing.T) { + testInput := `{"method":"POST","url":"https://music.163.com/api/song/enhance/player/url","params":{"ids":"[28445807,509098750,28528420]","br":999000}}` + rs := doHttp("POST", "https://music.163.com/api/linux/forward", testInput, "linux") + fmt.Println(rs) +} + +func TestGetSongUrl(t *testing.T) { + rs := GetSongUrl([]string{"22730174"}, 320) + fmt.Println(rs) +} + +func TestExp(t *testing.T) { + rs := doHttp("GET", "https://music.163.com/song?id=28445807", "", "") + reg, _ := regexp.Compile(`"title": "[^"]+`) + fmt.Println(reg.FindAllString(rs, 1)) + +} + +func TestSearch(t *testing.T) { + rs := Search("claris") + fmt.Println(rs) + var val songBean.SongData = rs[0] + fmt.Println(val.GetUrl(320)) +} + +// func TestAesEncryptCBC(t *testing.T) { +// fmt.Println(aesEncryptCBC("aaabaaabaaabaaab")) +// } diff --git a/netease/myCrypto.go b/netease/myCrypto.go new file mode 100644 index 0000000..e5f0415 --- /dev/null +++ b/netease/myCrypto.go @@ -0,0 +1,46 @@ +package netease + +import ( + "encoding/base64" + "encoding/hex" + "math/rand" + "strings" +) + +const ( + iv = "0102030405060708" + presetKey = "0CoJUm6Qyw8W8jud" + linuxapiKey = "rFgB&h#%2?^eDg:Q" + base62 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + public_key = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgtQn2JZ34ZC28NWYpAUd98iZ37BUrX/aKzmFbt7clFSs6sXqHauqKWqdtLkF2KexO40H1YTX8z2lSgBBOAxLsvaklV8k4cBFK9snQXE9/DDaFt6Rr7iVZMldczhC0JNgTz+SHXT6CBHuX3e9SdB1Ua44oncaTWz7OBGLbCiK45wIDAQAB\n-----END PUBLIC KEY-----" + eapiKey = "e82ckenh8dichen8" + modulus = "00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7" + + "b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280" + + "104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932" + + "575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b" + + "3ece0462db0a22b8e7" +) + +func weapi(text string) (rs map[string]string, err error) { + secretKey := make([]byte, 16) + for i := 0; i < 16; i++ { + secretKey[i] = byte(base62[rand.Int31n(62)]) + } + param := base64.StdEncoding.EncodeToString(AesEncryptCBC([]byte(text), []byte(iv), []byte(presetKey))) + param = base64.StdEncoding.EncodeToString(AesEncryptCBC([]byte(param), []byte(iv), secretKey)) + for i, j := 0, 15; i < j; i++ { + secretKey[i], secretKey[j] = secretKey[j], secretKey[i] + j-- + } + data := rsaEncrypt(secretKey) + rs = make(map[string]string) + rs["params"], rs["encSecKey"] = param, data + return rs, nil +} + +func linuxApi(text string) map[string]string { + rs := AesEncryptECB([]byte(text), []byte(linuxapiKey)) + ret := make(map[string]string) + ret["eparams"] = strings.ToUpper(hex.EncodeToString(rs)) + return ret +} diff --git a/songBean/bean.go b/songBean/bean.go new file mode 100644 index 0000000..883f2f4 --- /dev/null +++ b/songBean/bean.go @@ -0,0 +1,22 @@ +package songBean + +// 这个是用来放回歌曲文件的解析度,文件名, +// 文件大小,和文件的链接 +type SongInfo struct { + SongName string + SongUrl string + SongBr int + SongSize int +} + +// 这个是统一的搜索返回结果都要实现的接口,方便统一管理 +type SongData interface { + // 获取歌曲链接 + GetUrl(br int) SongInfo + // 获取歌曲名称 + GetFileName() string + // 获取歌手名字 + GetArtistName() string + // 获取歌单名字 + GetAlbumName() string +} diff --git a/wait.md b/wait.md new file mode 100644 index 0000000..ce62799 --- /dev/null +++ b/wait.md @@ -0,0 +1,10 @@ +# wait + +## kugou + +`http://songsearch.kugou.com/song_search_v2?keyword=claris` +`http://trackercdn.kugou.com/i/v2/?key={md5(hash+'kgcloudv2')}&hash={hash}&br=hq&appid=1005&pid=2&cmd=25&behavior=play` + +## kuwo + +`http://antiserver.kuwo.cn/anti.s?type=convert_url&format=mp3&response=url&rid=MUSIC_6949578`