From f4935846e83a7bfb490948702aa385ddc7206516 Mon Sep 17 00:00:00 2001 From: Sayem Chowdhury Date: Fri, 27 Sep 2024 00:56:46 +0600 Subject: [PATCH] update decrypt --- .DS_Store | Bin 6148 -> 6148 bytes go.mod | 1 + go.sum | 2 + pkg/decrypt/decrypt.go | 45 ++++++++++++++------ pkg/download/download.go | 88 +++++++++++++++++++++++++++++++++++++++ pkg/request/request.go | 13 +++++- pkg/request/types.go | 3 +- 7 files changed, 137 insertions(+), 15 deletions(-) create mode 100644 pkg/download/download.go diff --git a/.DS_Store b/.DS_Store index 972c57a8a7176b8de43102cfcb24788f18abdb49..9bb61e3a46e7d305effe9a68766900c19d82df83 100644 GIT binary patch literal 6148 zcmeHK%Wl&^6g`ti<4}RJ0I5q$Y)ca;rHZ-s?3f5um9mB} z;RBG^u;3^79XK(=YE;jA+`hyzV+m9|T%GRB`)xFcs z*gNxsPux9C@a#}_Q{w_Yv$2%>hj!?Dfj^{IM&vP}hQo{^@?Bh3M6)unWt{R^J;ni_ zL`%I7SG^6(^KjagGp)O|zH*tFgp2}4fxlHiejjWWi4BeO1l6a5mAV2D3v?@ETmAVb z+R{O6Xq+cV4^1SY#3WScD~8Z_2a$wxyrKE?1e0(GwKC_?D+_%?5&G&OY(dudJv8%2K*?Y#qrg8^;5W^}+3f%T delta 110 zcmZoMXfc=|#>AjHu~68Yk%57MnW2Con<2d{xF|0tKQA390up6lU;>j69#BMp0U^Ix skVA}RvjRsy^JaDqeh#2vK#}jvllesy85t*=iYQO^=i%5KBeH@S06}LId;kCd diff --git a/go.mod b/go.mod index d2440fd..089ed20 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/dgraph-io/badger/v4 v4.3.0 github.com/go-resty/resty/v2 v2.15.2 github.com/stretchr/testify v1.9.0 + golang.org/x/crypto v0.27.0 ) require ( diff --git a/go.sum b/go.sum index 9d2d61f..b4850b0 100644 --- a/go.sum +++ b/go.sum @@ -72,6 +72,8 @@ go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= diff --git a/pkg/decrypt/decrypt.go b/pkg/decrypt/decrypt.go index 1d9491b..ad6a8df 100644 --- a/pkg/decrypt/decrypt.go +++ b/pkg/decrypt/decrypt.go @@ -2,9 +2,12 @@ package decrypt import ( "crypto/aes" + "crypto/cipher" "crypto/md5" "encoding/hex" "fmt" + + "golang.org/x/crypto/blowfish" ) type TrackType struct { @@ -19,23 +22,41 @@ func Md5Hash(data string) string { } func GetSongFileName(track *TrackType, quality int) string { + // Step 1: Combine track details into a specific format step1 := fmt.Sprintf("%s¤%d¤%s¤%s", track.MD5_ORIGIN, quality, track.SNG_ID, track.MEDIA_VERSION) + // Step 2: Generate MD5 hash and concatenate it with step1, ensuring it's a multiple of 16 bytes step2 := Md5Hash(step1) + "¤" + step1 + "¤" for len(step2)%16 > 0 { step2 += " " } + // AES-128-ECB encryption cipherKey := []byte("jo6aey6haid2Teih") block, err := aes.NewCipher(cipherKey) if err != nil { panic(err) } - cipherText := make([]byte, len(step2)) - block.Encrypt(cipherText, []byte(step2)) + // Encrypt in ECB mode manually since Go's AES doesn't support ECB directly + encrypted := make([]byte, len(step2)) + encryptECB(block, []byte(step2), encrypted) - return hex.EncodeToString(cipherText) + return hex.EncodeToString(encrypted) +} + +// ECB mode encryption helper function since Go's crypto library doesn't directly support ECB. +func encryptECB(block cipher.Block, src, dst []byte) { + bs := block.BlockSize() + if len(src)%bs != 0 { + panic("plaintext is not a multiple of the block size") + } + + for len(src) > 0 { + block.Encrypt(dst, src[:bs]) + src = src[bs:] + dst = dst[bs:] + } } func GetBlowfishKey(trackID string) string { @@ -49,15 +70,16 @@ func GetBlowfishKey(trackID string) string { } func DecryptChunk(chunk []byte, blowfishKey string) []byte { - block, err := aes.NewCipher([]byte(blowfishKey)) + iv := []byte{0, 1, 2, 3, 4, 5, 6, 7} + block, err := blowfish.NewCipher([]byte(blowfishKey)) if err != nil { panic(err) } - cipherText := make([]byte, len(chunk)) - block.Decrypt(cipherText, chunk) - - return cipherText + dst := make([]byte, len(chunk)) + mode := cipher.NewCBCDecrypter(block, iv) + mode.CryptBlocks(dst, chunk) + return dst } func DecryptDownload(source []byte, trackID string) []byte { @@ -81,14 +103,13 @@ func DecryptDownload(source []byte, trackID string) []byte { chunk := make([]byte, chunkSize) copy(chunk, source[position:position+chunkSize]) - var chunkString string if i%3 > 0 || chunkSize < 2048 { - chunkString = string(chunk) + copy(destBuffer[position:], chunk) } else { - chunkString = string(DecryptChunk(chunk, blowfishKey)) + decryptedChunk := DecryptChunk(chunk, blowfishKey) + copy(destBuffer[position:], decryptedChunk) } - copy(destBuffer[position:position+len(chunkString)], chunkString) position += chunkSize i++ } diff --git a/pkg/download/download.go b/pkg/download/download.go new file mode 100644 index 0000000..d216ae1 --- /dev/null +++ b/pkg/download/download.go @@ -0,0 +1,88 @@ +package download + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/d-fi/GoFi/pkg/api" + "github.com/d-fi/GoFi/pkg/decrypt" + "github.com/d-fi/GoFi/pkg/types" + "github.com/go-resty/resty/v2" +) + +var client = resty.New() + +// Path to save music, update this to match your configuration +var MusicPath = "./music" // Change this to the desired path for storing music files + +// DownloadTrack downloads a track, adds metadata, and saves it to disk. +func DownloadTrack(sngID string, quality int, ext string, coverSize int) (string, error) { + // Fetch track information + track, err := api.GetTrackInfo(sngID) + if err != nil { + return "", fmt.Errorf("failed to fetch track info: %v", err) + } + + // Create directory if it does not exist + qualityPath := filepath.Join(MusicPath, fmt.Sprintf("%d", quality)) + if _, err := os.Stat(qualityPath); os.IsNotExist(err) { + if err := os.MkdirAll(qualityPath, 0755); err != nil { + return "", fmt.Errorf("failed to create directory: %v", err) + } + } + + // Get the track download URL + trackData, err := GetTrackDownloadUrl(track, quality) + if err != nil || trackData == nil { + return "", fmt.Errorf("failed to retrieve downloadable URL: %v", err) + } + + // Set up the save path for the track + safeTitle := strings.ReplaceAll(track.SNG_TITLE, "/", "_") + savedPath := filepath.Join(qualityPath, fmt.Sprintf("%s-%s.%s", safeTitle, track.SNG_ID, ext)) + + // Check if the file exists and update its timestamp if it does + if _, err := os.Stat(savedPath); err == nil { + currentTime := time.Now().Local() + if err := os.Chtimes(savedPath, currentTime, currentTime); err != nil { + return "", fmt.Errorf("failed to update file timestamps: %v", err) + } + } else { + // Download the track + resp, err := client.R().Get(trackData.TrackUrl) + if err != nil { + return "", fmt.Errorf("failed to download track: %v", err) + } + + // Check if decryption is needed and add metadata + var trackBody []byte + if trackData.IsEncrypted { + trackBody = decrypt.DecryptDownload(resp.Body(), track.SNG_ID) + } else { + trackBody = resp.Body() + } + + // Add metadata to the track + trackWithMetadata, err := addTrackTags(trackBody, track, coverSize) + if err != nil { + return "", fmt.Errorf("failed to add metadata: %v", err) + } + + // Write the track to disk + if err := os.WriteFile(savedPath, trackWithMetadata, 0644); err != nil { + return "", fmt.Errorf("failed to save track: %v", err) + } + } + + return savedPath, nil +} + +// addTrackTags adds metadata to the track. Implement this function based on your metadata requirements. +func addTrackTags(body []byte, track types.TrackType, coverSize int) ([]byte, error) { + // Add track metadata handling code here + // This is a placeholder implementation. Replace with actual metadata tagging logic. + return body, nil +} diff --git a/pkg/request/request.go b/pkg/request/request.go index 7fd5cd3..53c5e7d 100644 --- a/pkg/request/request.go +++ b/pkg/request/request.go @@ -13,8 +13,17 @@ func checkResponse(data []byte) (json.RawMessage, error) { return nil, fmt.Errorf("failed to unmarshal API response: %v", err) } - if len(apiResponse.Error) > 0 { - return nil, fmt.Errorf("API error: %v", apiResponse.Error) + // Check if the response contains error data in different formats + switch errVal := apiResponse.Error.(type) { + case string: + return nil, fmt.Errorf("API error: %s", errVal) + case map[string]interface{}: + // Convert the map to a string for better error message readability + errorMessage := "" + for key, value := range errVal { + errorMessage += fmt.Sprintf("%s: %v, ", key, value) + } + return nil, fmt.Errorf("API error: %v", errorMessage) } return apiResponse.Results, nil diff --git a/pkg/request/types.go b/pkg/request/types.go index 5cb0258..5cf3f9a 100644 --- a/pkg/request/types.go +++ b/pkg/request/types.go @@ -3,8 +3,9 @@ package request import "encoding/json" type APIResponse struct { - Error []string `json:"error"` + Error interface{} `json:"error"` Results json.RawMessage `json:"results"` + Payload interface{} `json:"payload,omitempty"` } type PublicAPIResponseError struct {