Skip to content
Snippets Groups Projects
Commit 38f987e0 authored by Silvan's avatar Silvan
Browse files

fix

parent 90c6e20a
No related branches found
No related tags found
No related merge requests found
package config
import (
"flag"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"utils"
)
var (
Port = flag.String("p", "80:443", "specify server port,such as : \"80:443\"")
Source = flag.String("o", "kuwo:kugou", "specify server source,such as : \"kuwo:kugou\"")
CertFile = flag.String("c", "./server.crt", "specify server cert,such as : \"server.crt\"")
KeyFile = flag.String("k", "./server.key", "specify server cert key ,such as : \"server.key\"")
Mode=flag.String("m", "1", "specify running mode(1:hosts) ,such as : \"1\"")
)
func ValidParams() bool {
flag.Parse()
if flag.NArg() > 0 {
fmt.Println("--------------------Invalid Params------------------------")
fmt.Printf("Invalid params=%s, num=%d\n", flag.Args(), flag.NArg())
for i := 0; i < flag.NArg(); i++ {
fmt.Printf("arg[%d]=%s\n", i, flag.Arg(i))
}
}
//fmt.Println("--------------------Port------------------------")
ports := strings.Split(*Port, ":")
if len(ports) < 1 {
fmt.Printf("port param invalid: %v \n", *Port)
return false
}
for _, p := range ports {
//fmt.Println(p)
if m, _ := regexp.MatchString("^\\d+$", p); !m {
fmt.Printf("port param invalid: %v \n", *Port)
return false
}
}
//fmt.Println("--------------------Source------------------------")
sources := strings.Split(*Source, ":")
if len(sources) < 1 {
fmt.Printf("source param invalid: %v \n", *Source)
return false
}
//for _, p := range sources {
// fmt.Println(p)
//}
currentPath, error := utils.GetCurrentPath()
if error != nil {
fmt.Println(error)
currentPath = ""
}
//fmt.Println(currentPath)
certFile, _ := filepath.Abs(*CertFile)
keyFile, _ := filepath.Abs(currentPath + *KeyFile)
_, err := os.Open(certFile)
if err != nil {
certFile, _ = filepath.Abs(currentPath + *CertFile)
}
_, err = os.Open(keyFile)
if err != nil {
keyFile, _ = filepath.Abs(currentPath + *KeyFile)
}
*CertFile = certFile
*KeyFile = keyFile
return true
}
package host
import (
"bufio"
"config"
"fmt"
"io"
"net"
"os"
"path/filepath"
"runtime"
"strings"
)
var (
ProxyIp = "127.0.0.1"
ProxyDomain = map[string]string{
"music.163.com": "59.111.181.35",
"interface.music.163.com": "59.111.181.35",
"interface3.music.163.com": "59.111.181.35",
"apm.music.163.com": "59.111.181.35",
"apm3.music.163.com": "59.111.181.35",
}
HostDomain = map[string]string{
"music.163.com": "59.111.181.35",
"interface.music.163.com": "59.111.181.35",
}
//ProxyDomain = map[string]string{
// "music.163.com": "59.111.181.35",
// "interface.music.163.com": "59.111.181.35",
// "interface3.music.163.com": "59.111.181.35",
// "apm.music.163.com": "59.111.181.35",
// "apm3.music.163.com": "59.111.181.35",
// "music.httpdns.c.163.com": "59.111.181.35",
// "httpdns.n.netease.com": "59.111.179.213",
//}
)
func getWinSystemDir() string {
dir := ""
if runtime.GOOS == "windows" {
dir = os.Getenv("windir")
}
return dir
}
func fileExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
//if os.IsNotExist(err) {
// return false, nil
//}
return false, err
}
func appendToFile(fileName string, content string) error {
f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
fmt.Println("appendToFile cacheFileList.yml file create failed. err: " + err.Error())
} else {
defer f.Close()
//n, _ := f.Seek(0, io.SeekEnd)
//_, err = f.WriteAt([]byte(content), n)
_, err = f.WriteString(content)
if err != nil {
fmt.Println("appendToFile write file fail:", err)
return err
}
}
return err
}
func restoreHost(hostPath string) error {
return nil
}
func appendToHost(hostPath string) error {
content := " \n# UnblockNetEaseMusic(Go)\n"
for domain, _ := range HostDomain {
content += ProxyIp + " " + domain + "\n"
}
return appendToFile(hostPath, content)
}
func backupHost(hostPath string) (bool, error) {
containsProxyDomain := false
host, err := os.Open(hostPath)
if err != nil {
fmt.Println("open file fail:", err)
return containsProxyDomain, err
}
defer host.Close()
gBackup, err := os.Create(hostPath + ".gBackup")
if err != nil {
fmt.Println("Open write file fail:", err)
return containsProxyDomain, err
}
defer gBackup.Close()
br := bufio.NewReader(host)
for {
line, _, err := br.ReadLine()
if err == io.EOF {
break
}
if err != nil {
fmt.Println("read err:", err)
return containsProxyDomain, err
}
newLine := string(line)
if !containsProxyDomain {
if strings.Contains(strings.ToUpper(newLine), strings.ToUpper("UnblockNetEaseMusic")) {
containsProxyDomain = true
fmt.Println("Found UnblockNetEaseMusic Line")
}
for domain, _ := range ProxyDomain {
if strings.Contains(newLine, domain) {
containsProxyDomain = true
fmt.Println("Found ProxyDomain Line")
}
}
}
_, err = gBackup.WriteString(newLine + "\n")
if err != nil {
fmt.Println("write to file fail:", err)
return containsProxyDomain, err
}
}
return containsProxyDomain, nil
}
// Exclude UnblockNetEaseMusic related host
func excludeRelatedHost(hostPath string) error {
host, err := os.Create(hostPath)
if err != nil {
fmt.Println("open file fail:", err)
return err
}
defer host.Close()
gBackup, err := os.Open(hostPath + ".gBackup")
if err != nil {
fmt.Println("Open write file fail:", err)
return err
}
defer gBackup.Close()
br := bufio.NewReader(gBackup)
for {
line, _, err := br.ReadLine()
if err == io.EOF {
break
}
if err != nil {
fmt.Println("read err:", err)
return err
}
newLine := string(line)
needWrite := true
for domain, _ := range ProxyDomain {
if strings.Contains(newLine, domain) {
needWrite = false
break
}
}
if needWrite && strings.Contains(strings.ToUpper(newLine), strings.ToUpper("UnblockNetEaseMusic")) {
needWrite = false
}
if needWrite && len(strings.TrimSpace(newLine)) == 0 {
needWrite = false
}
if needWrite {
_, err = host.WriteString(newLine + "\n")
if err != nil {
fmt.Println("write to file fail:", err)
return err
}
}
}
return nil
}
func resolveIp(domain string) (ip string, err error) {
return "", nil
}
func resolveIps() error {
for domain, _ := range HostDomain {
rAddr, err := net.ResolveIPAddr("ip", domain)
if err != nil {
fmt.Printf("Fail to resolve %s, %s\n", domain, err)
return err
}
if len(rAddr.IP) == 0 {
fmt.Printf("Fail to resolve %s,IP nil\n", domain)
return fmt.Errorf("Fail to resolve %s,Ip length==0 \n", domain)
}
HostDomain[domain] = rAddr.IP.String()
}
return nil
}
func getHostsPath() (string, error) {
hostsPath := "/etc/hosts"
if runtime.GOOS == "windows" {
hostsPath = getWinSystemDir()
hostsPath = filepath.Join(hostsPath, "system32", "drivers", "etc", "hosts")
} else {
hostsPath = filepath.Join(hostsPath)
}
if exist, err := fileExists(hostsPath); !exist {
fmt.Println("Not Found Host File:", hostsPath)
return hostsPath, err
}
return hostsPath, nil
}
func InitHosts() error {
fmt.Println("-------------------Init Host-------------------")
if *config.Mode == "1" { //hosts mode
hostsPath, err := getHostsPath()
if err == nil {
containsProxyDomain := false
containsProxyDomain, err = backupHost(hostsPath)
if err == nil {
if containsProxyDomain {
if err = excludeRelatedHost(hostsPath); err == nil {
err = resolveIps()
if err != nil {
return err
}
fmt.Println("HostDomain:", HostDomain)
}
} else {
err = resolveIps()
if err != nil {
return err
}
fmt.Println("HostDomain:", HostDomain)
}
if err = appendToHost(hostsPath); err == nil {
}
}
}
return err
} else {
err := resolveIps()
if err != nil {
return err
}
fmt.Println("HostDomain:", HostDomain)
return err
}
}
package network
import (
"bytes"
"compress/gzip"
"crypto/tls"
"fmt"
host2 "host"
"io"
"io/ioutil"
"net"
"net/http"
"strings"
"time"
)
type Netease struct {
Path string
Params string
JsonBody map[string]interface{}
}
type ClientRequest struct {
Method string
RemoteUrl string
Host string
Header http.Header
Body io.Reader
Proxy bool
}
func Request(clientRequest *ClientRequest) (*http.Response, error) {
//fmt.Println(clientRequest.RemoteUrl)
method := clientRequest.Method
remoteUrl := clientRequest.RemoteUrl
host := clientRequest.Host
header := clientRequest.Header
body := clientRequest.Body
proxy := clientRequest.Proxy
var resp *http.Response
request, err := http.NewRequest(method, remoteUrl, body)
if err != nil {
fmt.Printf("NewRequest fail:%v\n", err)
return resp, nil
}
request.URL.RawQuery = request.URL.Query().Encode()
if header != nil {
request.Header = header
}
c := http.Client{}
tr := http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
if len(host) > 0 {
request.Host = host
request.Header.Set("host", host)
}
if len(request.URL.Scheme) == 0 {
if request.TLS != nil {
request.URL.Scheme = "https"
} else {
request.URL.Scheme = "http"
}
}
if proxy && (request.URL.Scheme == "https" || request.TLS != nil) {
tr.TLSClientConfig = &tls.Config{}
// verify music.163.com certificate
tr.TLSClientConfig.ServerName = request.Host //it doesn't contain any IP SANs
// redirect to music.163.com will need verify self
if _, ok := host2.HostDomain[request.Host]; ok {
tr.TLSClientConfig.InsecureSkipVerify = true
}
}
c.Transport = &tr
if !proxy {
request.Header.Set("accept", "application/json, text/plain, */*")
request.Header.Set("accept-encoding", "gzip, deflate")
request.Header.Set("accept-language", "zh-CN,zh;q=0.9")
request.Header.Set("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36")
}
resp, err = c.Do(request)
if err != nil {
fmt.Println(request.Method, request.URL.String(), host)
fmt.Printf("http.Client.Do fail:%v\n", err)
return resp, err
}
return resp, err
}
func GetResponseBody(resp *http.Response, keepBody bool) ([]byte, error) {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("read body fail")
return body, err
}
resp.Body.Close()
if keepBody {
bodyHold := ioutil.NopCloser(bytes.NewBuffer(body))
resp.Body = bodyHold
}
encode := resp.Header.Get("Content-Encoding")
enableGzip := false
if len(encode) > 0 && (strings.Contains(encode, "gzip") || strings.Contains(encode, "deflate")) {
enableGzip = true
}
if enableGzip {
resp.Header.Del("Content-Encoding")
r, err := gzip.NewReader(bytes.NewReader(body))
if err != nil {
fmt.Println("read gzip body fail")
return body, err
}
defer r.Close()
body, err = ioutil.ReadAll(r)
if err != nil {
fmt.Println("read body fail")
return body, err
}
}
return body, err
}
package crypto
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"io"
)
// =================== CBC ======================
func AesEncryptCBC(origData []byte, key []byte) (encrypted []byte) {
// 分组秘钥
// NewCipher该函数限制了输入k的长度必须为16, 24或者32
block, _ := aes.NewCipher(key)
blockSize := block.BlockSize() // 获取秘钥块的长度
origData = pkcs5Padding(origData, blockSize) // 补全码
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) // 加密模式
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,success bool) {
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() {
if be > len(encrypted) {
return encrypted,false
}
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],true
}
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 {
return []byte{}
}
encrypted = make([]byte, aes.BlockSize+len(origData))
iv := encrypted[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return encrypted
}
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 {
fmt.Println("ciphertext too short")
return []byte{}
}
iv := encrypted[:aes.BlockSize]
encrypted = encrypted[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(encrypted, encrypted)
return encrypted
}
package processor
import (
"bufio"
"bytes"
"compress/gzip"
"crypto/md5"
"encoding/hex"
"encoding/json"
"fmt"
"host"
"io"
"io/ioutil"
"net/http"
"network"
"processor/crypto"
"provider"
"regexp"
"strings"
"utils"
)
var (
eApiKey = "e82ckenh8dichen8"
linuxApiKey = "rFgB&h#%2?^eDg:Q"
///api/song/enhance/player/url
///eapi/mlivestream/entrance/playlist/get
Path = map[string]int{
"/api/v3/playlist/detail": 1,
"/api/v3/song/detail": 1,
"/api/v6/playlist/detail": 1,
"/api/album/play": 1,
"/api/artist/privilege": 1,
"/api/album/privilege": 1,
"/api/v1/artist": 1,
"/api/v1/artist/songs": 1,
"/api/artist/top/song": 1,
"/api/v1/album": 1,
"/api/album/v3/detail": 1,
"/api/playlist/privilege": 1,
"/api/song/enhance/player/url": 1,
"/api/song/enhance/player/url/v1": 1,
"/api/song/enhance/download/url": 1,
"/batch": 1,
"/api/batch": 1,
"/api/v1/search/get": 1,
"/api/cloudsearch/pc": 1,
"/api/v1/playlist/manipulate/tracks": 1,
"/api/song/like": 1,
"/api/v1/play/record": 1,
"/api/playlist/v4/detail": 1,
"/api/v1/radio/get": 1,
"/api/v1/discovery/recommend/songs": 1,
"/api/cloudsearch/get/web": 1,
"/api/song/enhance/privilege": 1,
}
)
type Netease struct {
Path string
Params map[string]interface{}
JsonBody map[string]interface{}
Web bool
Encrypted bool
}
type MapType = map[string]interface{}
type SliceType = []interface{}
func RequestBefore(request *http.Request) *Netease {
netease := &Netease{Path: request.URL.Path}
if request.Method == http.MethodPost && (strings.Contains(netease.Path, "/eapi/") || strings.Contains(netease.Path, "/api/linux/forward")) {
request.Header.Del("x-napm-retry")
request.Header.Set("X-Real-IP", "118.66.66.66")
requestBody, _ := ioutil.ReadAll(request.Body)
requestHold := ioutil.NopCloser(bytes.NewBuffer(requestBody))
request.Body = requestHold
pad := make([]byte, 0)
if matched, _ := regexp.Match("/%0 +$/", requestBody); matched {
pad = requestBody
}
if netease.Path == "/api/linux/forward" {
requestBodyH := make([]byte, len(requestBody))
length, _ := hex.Decode(requestBodyH, requestBody[8:len(requestBody)-len(pad)])
decryptECBBytes, _ := crypto.AesDecryptECB(requestBodyH[:length], []byte(linuxApiKey))
var result MapType
d := json.NewDecoder(bytes.NewReader(decryptECBBytes))
d.UseNumber()
d.Decode(&result)
if utils.Exist("url", result) && utils.Exist("path", result["url"].(MapType)) {
netease.Path = result["url"].(MapType)["path"].(string)
}
netease.Params = utils.ParseJson(bytes.NewBufferString(result["params"].(string)).Bytes())
fmt.Println("forward")
//fmt.Printf("path:%s \nparams:%s\n", netease.Path, netease.Params)
} else {
requestBodyH := make([]byte, len(requestBody))
length, _ := hex.Decode(requestBodyH, requestBody[7:len(requestBody)-len(pad)])
decryptECBBytes, _ := crypto.AesDecryptECB(requestBodyH[:length], []byte(eApiKey))
decryptString := string(decryptECBBytes)
data := strings.Split(decryptString, "-36cd479b6b5-")
netease.Path = data[0]
netease.Params = utils.ParseJson(bytes.NewBufferString(data[1]).Bytes())
//fmt.Printf("path:%s \nparams:%s\n", netease.Path, netease.Params)
}
} else if strings.Index(netease.Path, "/weapi/") == 0 || strings.Index(netease.Path, "/api/") == 0 {
request.Header.Set("X-Real-IP", "118.66.66.66")
netease.Web = true
netease.Path = utils.ReplaceAll(netease.Path, `^\/weapi\/`, "/api/")
netease.Path = utils.ReplaceAll(netease.Path, `\?.+$`, "")
netease.Path = utils.ReplaceAll(netease.Path, `\/\d*$`, "")
} else if strings.Contains(netease.Path, "package") {
}
return netease
}
func Request(request *http.Request, remoteUrl string) (*http.Response, error) {
clientRequest := network.ClientRequest{
Method: request.Method,
RemoteUrl: remoteUrl,
Host: request.Host,
Header: request.Header,
Body: request.Body,
Proxy: true,
}
return network.Request(&clientRequest)
}
func RequestAfter(request *http.Request, response *http.Response, netease *Netease) {
run := false
if _, ok := Path[netease.Path]; ok {
run = true
}
if run && response.StatusCode == 200 {
encode := response.Header.Get("Content-Encoding")
enableGzip := false
if len(encode) > 0 && (strings.Contains(encode, "gzip") || strings.Contains(encode, "deflate")) {
enableGzip = true
}
body, _ := ioutil.ReadAll(response.Body)
tmpBody := make([]byte, len(body))
copy(tmpBody, body)
if len(body) > 0 {
decryptECBBytes := body
if enableGzip {
r, _ := gzip.NewReader(bytes.NewReader(decryptECBBytes))
defer r.Close()
decryptECBBytes, _ = ioutil.ReadAll(r)
}
//fmt.Println(string(decryptECBBytes), netease)
decryptECBBytes, encrypted := crypto.AesDecryptECB(decryptECBBytes, []byte(eApiKey))
netease.Encrypted = encrypted
result := utils.ParseJson(decryptECBBytes)
netease.JsonBody = result
//fmt.Println(utils.ToJson(result))
modified := false
code := netease.JsonBody["code"].(json.Number).String()
//fmt.Println(netease)
if !netease.Web && (code == "401" || code == "512") && strings.Contains(netease.Path, "manipulate") {
modified = tryCollect(netease, request)
} else if !netease.Web && (code == "401" || code == "512") && strings.EqualFold(netease.Path, "/api/song/like") {
modified = tryLike(netease, request)
} else if strings.Contains(netease.Path, "url") {
modified = tryMatch(netease)
fmt.Println(utils.ToJson(netease.JsonBody))
}
if processMapJson(netease.JsonBody) || modified {
response.Header.Del("transfer-encoding")
response.Header.Del("content-encoding")
response.Header.Del("content-length")
//netease.JsonBody = netease.JsonBody
fmt.Println("NeedRepackage")
modifiedJson, _ := json.Marshal(netease.JsonBody)
//fmt.Println(netease)
//fmt.Println(string(modifiedJson))
if netease.Encrypted {
modifiedJson = crypto.AesEncryptECB(modifiedJson, []byte(eApiKey))
}
response.Body = ioutil.NopCloser(bytes.NewBuffer(modifiedJson))
} else {
fmt.Println("NotNeedRepackage")
responseHold := ioutil.NopCloser(bytes.NewBuffer(tmpBody))
response.Body = responseHold
}
} else {
responseHold := ioutil.NopCloser(bytes.NewBuffer(tmpBody))
response.Body = responseHold
}
} else {
//fmt.Println("Not Process")
}
}
func tryCollect(netease *Netease, request *http.Request) bool {
modified := false
//fmt.Println(utils.ToJson(netease))
if utils.Exist("trackIds", netease.Params) {
trackId := ""
switch netease.Params["trackIds"].(type) {
case string:
var result SliceType
d := json.NewDecoder(bytes.NewReader(bytes.NewBufferString(netease.Params["trackIds"].(string)).Bytes()))
d.UseNumber()
d.Decode(&result)
trackId = result[0].(string)
case SliceType:
trackId = netease.Params["trackIds"].(SliceType)[0].(json.Number).String()
}
pid := netease.Params["pid"].(string)
op := netease.Params["op"].(string)
proxyRemoteHost := host.ProxyDomain["music.163.com"]
clientRequest := network.ClientRequest{
Method: http.MethodPost,
Host: "music.163.com",
RemoteUrl: "http://" + proxyRemoteHost + "/api/playlist/manipulate/tracks",
Header: request.Header,
Body: ioutil.NopCloser(bytes.NewBufferString("trackIds=[" + trackId + "," + trackId + "]&pid=" + pid + "&op=" + op)),
}
resp, err := network.Request(&clientRequest)
if err != nil {
return modified
}
body, err := network.GetResponseBody(resp, false)
if err != nil {
return modified
}
netease.JsonBody = utils.ParseJson(body)
modified = true
}
return modified
}
func tryLike(netease *Netease, request *http.Request) bool {
//fmt.Println("try like")
modified := false
if utils.Exist("trackId", netease.Params) {
trackId := netease.Params["trackId"].(string)
proxyRemoteHost := host.ProxyDomain["music.163.com"]
clientRequest := network.ClientRequest{
Method: http.MethodGet,
Host: "music.163.com",
RemoteUrl: "http://" + proxyRemoteHost + "/api/v1/user/info",
Header: request.Header}
resp, err := network.Request(&clientRequest)
if err != nil {
return modified
}
body, err := network.GetResponseBody(resp, false)
if err != nil {
return modified
}
jsonBody := utils.ParseJson(body)
if utils.Exist("userPoint", jsonBody) && utils.Exist("userId", jsonBody["userPoint"].(MapType)) {
userId := jsonBody["userPoint"].(MapType)["userId"].(json.Number).String()
clientRequest.RemoteUrl = "http://" + proxyRemoteHost + "/api/user/playlist?uid=" + userId + "&limit=1"
resp, err = network.Request(&clientRequest)
if err != nil {
return modified
}
body, err = network.GetResponseBody(resp, false)
if err != nil {
return modified
}
jsonBody = utils.ParseJson(body)
if utils.Exist("playlist", jsonBody) {
pid := jsonBody["playlist"].(SliceType)[0].(MapType)["id"].(json.Number).String()
clientRequest.Method = http.MethodPost
clientRequest.RemoteUrl = "http://" + proxyRemoteHost + "/api/playlist/manipulate/tracks"
clientRequest.Body = ioutil.NopCloser(bytes.NewBufferString("trackIds=[" + trackId + "," + trackId + "]&pid=" + pid + "&op=add"))
resp, err = network.Request(&clientRequest)
if err != nil {
return modified
}
body, err = network.GetResponseBody(resp, false)
if err != nil {
return modified
}
jsonBody = utils.ParseJson(body)
code := jsonBody["code"].(json.Number).String()
if code == "200" || code == "502" {
netease.JsonBody = make(MapType)
netease.JsonBody["code"] = 200
netease.JsonBody["playlistId"] = pid
modified = true
}
}
}
}
return modified
}
func tryMatch(netease *Netease) bool {
fmt.Println(netease.Path)
modified := false
jsonBody := netease.JsonBody
if value, ok := jsonBody["data"]; ok {
switch value.(type) {
case SliceType:
if strings.Contains(netease.Path, "download") {
for index, data := range value.(SliceType) {
if index == 0 {
modified = searchGreySong(data.(MapType), netease) || modified
break
}
}
} else {
modified = searchGreySongs(value.(SliceType), netease) || modified
}
case MapType:
modified = searchGreySong(value.(MapType), netease) || modified
default:
}
}
//modifiedJson, _ := json.Marshal(jsonBody)
//fmt.Println(string(modifiedJson))
return modified
}
func searchGreySongs(data SliceType, netease *Netease) bool {
modified := false
for _, value := range data {
switch value.(type) {
case MapType:
modified = searchGreySong(value.(MapType), netease) || modified
}
}
return modified
}
func searchGreySong(data MapType, netease *Netease) bool {
modified := false
if data["url"] == nil {
data["flag"] = 0
songId := data["id"].(json.Number).String()
song := provider.Find(songId)
haveSongMd5 := false
if song.Size > 0 {
modified = true
if song.Br == 999000 {
data["type"] = "flac"
} else {
data["type"] = "mp3"
}
data["encodeType"] = data["type"] //web
data["level"] = "standard" //web
data["fee"] = 8 //web
data["url"] = song.Url
if len(song.Md5) > 0 {
data["md5"] = song.Md5
haveSongMd5 = true
} else {
h := md5.New()
h.Write([]byte(song.Url))
data["md5"] = hex.EncodeToString(h.Sum(nil))
haveSongMd5 = false
}
if song.Br > 0 {
data["br"] = song.Br
} else {
data["br"] = 128000
}
data["size"] = song.Size
data["freeTrialInfo"] = nil
data["code"] = 200
if strings.Contains(netease.Path, "download") { //calculate the file md5
if !haveSongMd5 {
data["md5"] = calculateSongMd5(songId, song.Url)
}
} else if !haveSongMd5 {
go calculateSongMd5(songId, song.Url)
}
}
}
return modified
}
func calculateSongMd5(songId string, songUrl string) string {
songMd5 := ""
clientRequest := network.ClientRequest{
Method: http.MethodGet,
RemoteUrl: songUrl,
}
resp, err := network.Request(&clientRequest)
if err != nil {
fmt.Println(err)
return songMd5
}
r := bufio.NewReader(resp.Body)
h := md5.New()
_, err = io.Copy(h, r)
if err != nil {
fmt.Println(err)
return songMd5
}
songMd5 = hex.EncodeToString(h.Sum(nil))
provider.UpdateCacheMd5(songId, songMd5)
//fmt.Println("calculateSongMd5 songId:", songId, ",songUrl:", songUrl, ",md5:", songMd5)
return songMd5
}
func processSliceJson(jsonSlice SliceType) bool {
needModify := false
for _, value := range jsonSlice {
switch value.(type) {
case MapType:
needModify = processMapJson(value.(MapType)) || needModify
case SliceType:
needModify = processSliceJson(value.(SliceType)) || needModify
default:
//fmt.Printf("index(%T):%v\n", index, index)
//fmt.Printf("value(%T):%v\n", value, value)
}
}
return needModify
}
func processMapJson(jsonMap MapType) bool {
needModify := false
if utils.Exists([]string{"st", "subp", "pl", "dl"}, jsonMap) {
if v, _ := jsonMap["st"]; v.(json.Number).String() != "0" {
//open grep song
jsonMap["st"] = 0
needModify = true
}
if v, _ := jsonMap["subp"]; v.(json.Number).String() != "1" {
jsonMap["subp"] = 1
needModify = true
}
if v, _ := jsonMap["pl"]; v.(json.Number).String() == "0" {
jsonMap["pl"] = 320000
needModify = true
}
if v, _ := jsonMap["dl"]; v.(json.Number).String() == "0" {
jsonMap["dl"] = 320000
needModify = true
}
}
for _, value := range jsonMap {
switch value.(type) {
case MapType:
needModify = processMapJson(value.(MapType)) || needModify
case SliceType:
needModify = processSliceJson(value.(SliceType)) || needModify
default:
//if key == "fee" && value.(json.Number).String() != "0" {
// jsonMap[key] = 0
// needModify = true
//}
}
}
return needModify
}
package kuwo
import (
"fmt"
"net/http"
"net/url"
"network"
"strings"
"utils"
)
func SearchSong(key map[string]interface{}) string {
keyword := key["keyword"].(string)
token := getToken(keyword)
header := make(http.Header, 3)
header["referer"] = append(header["referer"], "http://www.kuwo.cn/search/list?key="+url.QueryEscape(keyword))
header["csrf"] = append(header["csrf"], token)
header["cookie"] = append(header["cookie"], "kw_token="+token)
clientRequest := network.ClientRequest{
Method: http.MethodGet,
RemoteUrl: "http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key=" + keyword + "&pn=1&rn=30",
Host: "kuwo.cn",
Header: header,
Proxy: false,
}
resp, err := network.Request(&clientRequest)
if err != nil {
fmt.Println(err)
return ""
}
body, err := network.GetResponseBody(resp, false)
if err != nil {
fmt.Println(err)
return ""
}
result := utils.ParseJson(body)
var musicId = ""
if result["data"] != nil && result["data"].(map[string]interface{}) != nil && len(result["data"].(map[string]interface{})["list"].([]interface{})) > 0 {
matched := result["data"].(map[string]interface{})["list"].([]interface{})[0]
if matched != nil && matched.(map[string]interface{})["musicrid"] != nil {
musicrid := matched.(map[string]interface{})["musicrid"].(string)
musicSlice := strings.Split(musicrid, "_")
musicId = musicSlice[len(musicSlice)-1]
}
}
if len(musicId) > 0 {
clientRequest := network.ClientRequest{
Method: http.MethodGet,
RemoteUrl: "http://antiserver.kuwo.cn/anti.s?type=convert_url&format=mp3&response=url&rid=MUSIC_" + musicId,
Host: "antiserver.kuwo.cn",
Header: header,
Proxy: false,
}
resp, err := network.Request(&clientRequest)
if err != nil {
fmt.Println(err)
return ""
}
body, err = network.GetResponseBody(resp, false)
address := string(body)
if strings.Index(address, "http") == 0 {
return address
}
}
return ""
}
func getToken(keyword string) string {
var token = ""
clientRequest := network.ClientRequest{
Method: http.MethodGet,
RemoteUrl: "http://kuwo.cn/search/list?key=" + keyword,
Host: "kuwo.cn",
Header: nil,
Proxy: false,
}
resp, err := network.Request(&clientRequest)
if err != nil {
fmt.Println(err)
return token
}
defer resp.Body.Close()
cookies := resp.Header.Get("set-cookie")
if strings.Contains(cookies, "kw_token") {
cookies = utils.ReplaceAll(cookies, ";.*", "")
splitSlice := strings.Split(cookies, "=")
token = splitSlice[len(splitSlice)-1]
}
return token
}
package provider
import (
"bytes"
"encoding/json"
"fmt"
"host"
"net/http"
"network"
"provider/kuwo"
"strconv"
"strings"
"utils"
)
type Song struct {
Size int64
Br int
Url string
Md5 string
}
type MapType = map[string]interface{}
type SliceType = []interface{}
var cache = make(map[string]Song)
func UpdateCacheMd5(songId string, songMd5 string) {
if song, ok := cache[songId]; ok {
song.Md5 = songMd5
cache[songId] = song
//fmt.Println("update cache,songId:", songId, ",md5:", songMd5, utils.ToJson(song))
}
}
func Find(id string) Song {
fmt.Println("find song info,id:", id)
if song, ok := cache[id]; ok {
fmt.Println("hit cache:", utils.ToJson(song))
return song
}
var songT Song
clientRequest := network.ClientRequest{
Method: http.MethodGet,
RemoteUrl: "https://" + host.ProxyDomain["music.163.com"] + "/api/song/detail?ids=[" + id + "]",
Host: "music.163.com",
Header: nil,
Proxy: true,
}
resp, err := network.Request(&clientRequest)
if err != nil {
return songT
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
body, err2 := network.GetResponseBody(resp, false)
if err2 != nil {
fmt.Println("GetResponseBody fail")
return songT
}
var oJson MapType
d := json.NewDecoder(bytes.NewReader(body))
d.UseNumber()
d.Decode(&oJson)
if oJson["songs"] != nil {
song := oJson["songs"].(SliceType)[0]
var modifiedJson = make(MapType, 6)
var artists []string
switch song.(type) {
case MapType:
modifiedJson["id"] = song.(MapType)["id"]
modifiedJson["name"] = song.(MapType)["name"]
modifiedJson["alias"] = song.(MapType)["alias"]
modifiedJson["duration"] = song.(MapType)["duration"]
modifiedJson["album"] = make(MapType, 2)
modifiedJson["album"].(MapType)["id"] = song.(MapType)["album"].(MapType)["id"]
modifiedJson["album"].(MapType)["name"] = song.(MapType)["album"].(MapType)["name"]
switch song.(MapType)["artists"].(type) {
case SliceType:
length := len(song.(MapType)["artists"].(SliceType))
modifiedJson["artists"] = make(SliceType, length)
artists = make([]string, length)
for index, value := range song.(MapType)["artists"].(SliceType) {
if modifiedJson["artists"].(SliceType)[index] == nil {
modifiedJson["artists"].(SliceType)[index] = make(MapType, 2)
}
modifiedJson["artists"].(SliceType)[index].(MapType)["id"] = value.(MapType)["id"]
modifiedJson["artists"].(SliceType)[index].(MapType)["name"] = value.(MapType)["name"]
artists[index] = value.(MapType)["name"].(string)
}
}
default:
}
if modifiedJson["name"] != nil {
modifiedJson["name"] = utils.ReplaceAll(modifiedJson["name"].(string), `\s*cover[::\s][^)]+)`, "")
modifiedJson["name"] = utils.ReplaceAll(modifiedJson["name"].(string), `\(\s*cover[::\s][^\)]+\)`, "")
}
modifiedJson["keyword"] = modifiedJson["name"].(string) + " - " + strings.Join(artists, " / ")
songUrl := searchSong(modifiedJson)
if len(songUrl) > 0 { //未版权
songS := processSong(songUrl)
if songS.Size > 0 {
//fmt.Println(utils.ToJson(songS))
cache[id] = songS
return songS
}
}
//fmt.Println(utils.ToJson(modifiedJson))
return songT
} else {
return songT
}
} else {
return songT
}
}
func searchSong(key MapType) string {
//cache after
return kuwo.SearchSong(key)
}
func processSong(songUrl string) Song {
var song Song
if len(songUrl) > 0 {
header := make(http.Header, 1)
header["range"] = append(header["range"], "bytes=0-8191")
clientRequest := network.ClientRequest{
Method: http.MethodGet,
RemoteUrl: songUrl,
Header: header,
Proxy: false,
}
resp, err := network.Request(&clientRequest)
if err != nil {
fmt.Println("processSong fail:", err)
return song
}
if resp.StatusCode > 199 && resp.StatusCode < 300 {
if strings.Contains(songUrl, "qq.com") {
song.Md5 = resp.Header.Get("server-md5")
} else if strings.Contains(songUrl, "xiami.net") || strings.Contains(songUrl, "qianqian.com") {
song.Md5 = strings.ToLower(utils.ReplaceAll(resp.Header.Get("etag"), `/"/g`, ""))
//.replace(/"/g, '').toLowerCase()
}
size := resp.Header.Get("content-range")
if len(size) > 0 {
sizeSlice := strings.Split(size, "/")
if len(sizeSlice) > 0 {
size = sizeSlice[len(sizeSlice)-1]
}
} else {
size = resp.Header.Get("content-length")
if len(size) < 1 {
size = "0"
}
}
song.Size, _ = strconv.ParseInt(size, 10, 64)
song.Url = songUrl
if resp.Header.Get("content-length") == "8192" {
body, err := network.GetResponseBody(resp, false)
if err != nil {
fmt.Println("song GetResponseBody error:", err)
return song
}
bitrate := decodeBitrate(body)
if bitrate > 0 && bitrate < 500 {
song.Br = bitrate * 1000
}
}
//song.url = response.url.href
}
}
return song
}
func decodeBitrate(data []byte) int {
bitRateMap := map[int]map[int][]int{
0: {
3: {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 500},
2: {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 500},
1: {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 500},
},
3: {
3: {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 500},
2: {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 500},
1: {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 500},
},
2: {
3: {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 500},
2: {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 500},
1: {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 500},
},
}
var pointer = 0
if strings.EqualFold(string(data[0:4]), "fLaC") {
return 999
}
if strings.EqualFold(string(data[0:3]), "ID3") {
pointer = 6
var size = 0
for index, value := range data[pointer : pointer+4] {
size = size + int((value&0x7f)<<(7*(3-index)))
}
pointer = 10 + size
}
header := data[pointer : pointer+4]
// https://www.allegro.cc/forums/thread/591512/674023
if len(header) == 4 &&
header[0] == 0xff &&
((header[1]>>5)&0x7) == 0x7 &&
((header[1]>>1)&0x3) != 0 &&
((header[2]>>4)&0xf) != 0xf &&
((header[2]>>2)&0x3) != 0x3 {
version := (header[1] >> 3) & 0x3
layer := (header[1] >> 1) & 0x3
bitrate := header[2] >> 4
return bitRateMap[int(version)][int(layer)][int(bitrate)]
}
return 0
}
package proxy
import (
"bytes"
"config"
"crypto/tls"
"fmt"
"host"
"io"
"net"
"net/http"
"network"
"processor"
"strings"
"time"
"version"
)
type HttpHandler struct{}
func InitProxy() {
fmt.Println("-------------------Init Proxy-------------------")
go startTlsServer("0.0.0.0:443", *config.CertFile, *config.KeyFile, &HttpHandler{})
startServer("0.0.0.0:80", &HttpHandler{})
}
func (h *HttpHandler) ServeHTTP(resp http.ResponseWriter, request *http.Request) {
requestURI := request.RequestURI
path := request.URL.Path
rawQuery := request.URL.RawQuery
uriBytes := []byte(path)
left := uriBytes[:(len(uriBytes) / 2)]
right := uriBytes[len(uriBytes)/2:]
hostStr := request.URL.Host
//fmt.Println(request.URL.String(), ",", request.Method)
if len(hostStr) == 0 {
hostStr = request.Host
}
if len(request.URL.Port()) > 0 && strings.Contains(hostStr, ":"+request.URL.Port()) {
hostStr = strings.Replace(hostStr, ":"+request.URL.Port(), "", 1)
}
scheme := "http://"
if request.TLS != nil || request.URL.Port() == "443" {
scheme = "https://"
}
if len(request.URL.Scheme) > 0 {
scheme = request.URL.Scheme + "://"
}
if strings.Contains(hostStr, "localhost") || strings.Contains(hostStr, "127.0.0.1") || strings.Contains(hostStr, "0.0.0.0") || (len(path) > 1 && strings.Count(path, "/") > 1 && bytes.EqualFold(left, right)) {
//cause infinite loop
requestURI = scheme + request.Host
if bytes.EqualFold(left, right) {
requestURI += string(left)
} else {
requestURI += string(uriBytes)
}
fmt.Printf("Abandon:%s\n", requestURI)
resp.WriteHeader(200)
resp.Write([]byte(version.AppVersion()))
return
}
request.Host = hostStr
if proxyDomain, ok := host.ProxyDomain[hostStr]; ok && !strings.Contains(path, "stream") {
if request.Method == http.MethodConnect {
proxyConnectLocalhost(resp, request)
} else {
if *config.Mode != "1" {
proxyDomain = hostStr
} else if hostIp, ok := host.HostDomain[hostStr]; ok {
proxyDomain = hostIp
} else {
proxyDomain = hostStr
}
if len(request.URL.Port()) > 0 {
proxyDomain = proxyDomain + ":" + request.URL.Port()
}
urlString := scheme + proxyDomain + path
if len(rawQuery) > 0 {
urlString = urlString + "?" + rawQuery
}
fmt.Printf("Transport:%s(%s)(%s)\n", urlString, request.Host, request.Method)
netease := processor.RequestBefore(request)
//fmt.Printf("{path:%s,web:%v,encrypted:%v}\n", netease.Path, netease.Web, netease.Encrypted)
response, err := processor.Request(request, urlString)
if err != nil {
fmt.Println("Request error:", urlString)
return
}
defer response.Body.Close()
processor.RequestAfter(request, response, netease)
for name, values := range response.Header {
resp.Header()[name] = values
//fmt.Println(name,"=",values)
}
resp.WriteHeader(response.StatusCode)
_, err = io.Copy(resp, response.Body)
if err != nil {
fmt.Println("io.Copy error:", err)
return
}
defer response.Body.Close()
//resp.Write(body)
}
} else {
if request.Method == http.MethodConnect {
proxyConnect(resp, request)
} else {
if proxyDomain, ok := host.ProxyDomain[hostStr]; ok {
if len(request.URL.Port()) > 0 {
proxyDomain = proxyDomain + ":" + request.URL.Port()
}
requestURI = scheme + proxyDomain + path
} else {
if len(request.URL.Port()) > 0 {
hostStr = hostStr + ":" + request.URL.Port()
}
requestURI = scheme + hostStr + path
}
if len(rawQuery) > 0 {
requestURI = requestURI + "?" + rawQuery
}
//proxy := httputil.NewSingleHostReverseProxy(remote)
for hostDoman, _ := range host.HostDomain {
if strings.Contains(request.Referer(), hostDoman) {
request.Header.Set("referer", request.Host)
break
}
}
//for key, values := range request.Header {
// fmt.Println(key, "=", values)
//}
fmt.Printf("Direct:%s(%s)(%s)\n", requestURI, request.Host, request.Method)
response, err := network.Request(&network.ClientRequest{
Method: request.Method,
RemoteUrl: requestURI,
Host: request.Host,
Header: request.Header,
Body: request.Body,
Proxy: false,
})
if err != nil {
fmt.Println("network.Request error:", err)
return
}
for name, values := range response.Header {
resp.Header()[name] = values
//fmt.Println(name,"=",values)
}
resp.WriteHeader(response.StatusCode)
_, err = io.Copy(resp, response.Body)
if err != nil {
fmt.Println("io.Copy error:", err)
return
}
defer response.Body.Close()
//proxy.ServeHTTP(resp, request)
}
}
}
func proxyConnectLocalhost(rw http.ResponseWriter, req *http.Request) {
hij, ok := rw.(http.Hijacker)
if !ok {
fmt.Println("HTTP Server does not support hijacking")
}
client, _, err := hij.Hijack()
if err != nil {
fmt.Println(err)
return
}
localUrl := "localhost"
var server net.Conn
if req.URL.Port() == "80" {
localUrl = localUrl + ":80"
server, err = net.Dial("tcp", localUrl)
} else {
localUrl = localUrl + ":443"
server, err = tls.Dial("tcp", localUrl, &tls.Config{InsecureSkipVerify: true})
}
if err != nil {
fmt.Println(err)
return
}
client.Write([]byte("HTTP/1.0 200 Connection Established\r\n\r\n"))
go io.Copy(server, client)
io.Copy(client, server)
}
func proxyConnect(rw http.ResponseWriter, req *http.Request) {
fmt.Printf("Received request %s %s %s\n",
req.Method,
req.Host,
req.RemoteAddr,
)
if req.Method != "CONNECT" {
rw.WriteHeader(http.StatusMethodNotAllowed)
rw.Write([]byte("This is a http tunnel proxy, only CONNECT method is allowed."))
return
}
host := req.URL.Host
hij, ok := rw.(http.Hijacker)
if !ok {
fmt.Println("HTTP Server does not support hijacking")
}
client, _, err := hij.Hijack()
if err != nil {
fmt.Println(err)
return
}
server, err := net.Dial("tcp", host)
if err != nil {
fmt.Println(err)
return
}
client.Write([]byte("HTTP/1.0 200 Connection Established\r\n\r\n"))
go io.Copy(server, client)
io.Copy(client, server)
}
func startTlsServer(addr, certFile, keyFile string, handler http.Handler) {
fmt.Printf("starting TLS Server %s\n", addr)
s := &http.Server{
Addr: addr,
Handler: handler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
err := s.ListenAndServeTLS(certFile, keyFile)
if err != nil {
panic(err)
}
}
func startServer(addr string, handler http.Handler) {
fmt.Printf("starting Server %s\n", addr)
s := &http.Server{
Addr: addr,
Handler: handler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
err := s.ListenAndServe()
if err != nil {
panic(err)
}
}
package utils
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
)
func FormatMap(data map[string]interface{}) string {
format := ""
for key, value := range data {
format += fmt.Sprintf("%s=%v\n", key, value)
}
return format
}
func ReplaceAll(str string, expr string, replaceStr string) string {
reg := regexp.MustCompile(expr)
str = reg.ReplaceAllString(str, replaceStr)
return str
}
func ParseJson(data []byte) map[string]interface{} {
var result map[string]interface{}
d := json.NewDecoder(bytes.NewReader(data))
d.UseNumber()
d.Decode(&result)
return result
}
func ToJson(object interface{}) string {
json, err := json.Marshal(object)
if err != nil {
fmt.Println("ToJson Error:", err)
return "{}"
}
return string(json)
}
func Exists(keys []string, h map[string]interface{}) bool {
for _, key := range keys {
if !Exist(key, h) {
return false
}
}
return true
}
func Exist(key string, h map[string]interface{}) bool {
_, ok := h[key]
return ok
}
func GetCurrentPath() (string, error) {
file, err := exec.LookPath(os.Args[0])
if err != nil {
return "", err
}
path, err := filepath.Abs(file)
if err != nil {
return "", err
}
i := strings.LastIndex(path, "/")
if i < 0 {
i = strings.LastIndex(path, "\\")
}
if i < 0 {
return "", errors.New(`error: Can't find "/" or "\".`)
}
return string(path[0 : i+1]), nil
}
package version
import "fmt"
var (
Version string
//will be overwritten automatically by the build system
GitCommit string
GoVersion string
BuildTime string
)
func FullVersion() string {
return fmt.Sprintf("Version: %6s \nGit commit: %6s \nGo version: %6s \nBuild time: %6s \n",
Version, GitCommit, GoVersion, BuildTime)
}
func AppVersion() string {
return fmt.Sprintf(`
## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## ## ## ## ## ## ## ## ##
## ## ## ## ## #### ## ## ## ## ## ## ## ## ## ## ## ## ## ##
%s`+" by cnsilvan(https://github.com/cnsilvan/UnblockNeteaseMusic) \n", Version)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment