開發(fā)過程

1.安裝gin框架

go get -u github.com/gin-gonic/gin

2.封裝suno請(qǐng)求

1.相關(guān)結(jié)構(gòu)體

printf("hello world!");package internal

import "time"

// GenerateReq generate-Req
type GenerateReq struct {
GptDescriptionPrompt string json:"gpt_description_prompt" Prompt string json:"prompt" Mv string json:"mv" Title string json:"title" Tags string json:"tags" } // GenerateResp generate-Resp type GenerateResp struct { BatchSize int json:"batch_size" Clips []Clips json:"clips" CreatedAt time.Time json:"created_at" ID string json:"id" Status string json:"status" Metadata json:"metadata" MajorModelVersion string json:"major_model_version" } // TokenResp token-Response type TokenResp struct { Jwt string Object string } // SidResp session-resp type SidResp struct { Response struct { Object string json:"object" ID string json:"id" Sessions []Session json:"sessions" SignIn interface{} json:"sign_in" SignUp interface{} json:"sign_up" LastActiveSessionID string json:"last_active_session_id" CreatedAt time.Time json:"created_at" UpdatedAt time.Time json:"updated_at" } json:"response" Client interface{} json:"client" } // Clips clips type Clips struct { Detail string json:"detail" Id string json:"id" VideoUrl string json:"video_url" AudioUrl string json:"audio_url" ImageUrl string json:"image_url" ImageLargeUrl string json:"image_large_url" MajorModelVersion string json:"major_model_version" ModelName string json:"model_name" Metadata *Metadata json:"metadata" IsLiked bool json:"is_liked" UserId string json:"user_id" IsTrashed bool json:"is_trashed" Reaction interface{} json:"reaction" CreatedAt time.Time json:"created_at" Status string json:"status" Title string json:"title" PlayCount int json:"play_count" UpvoteCount int json:"upvote_count" IsPublic bool json:"is_public" } // Metadata Metadata type Metadata struct { Tags string json:"tags" Prompt string json:"prompt" GptDescriptionPrompt string json:"gpt_description_prompt" AudioPromptId interface{} json:"audio_prompt_id" History interface{} json:"history" ConcatHistory interface{} json:"concat_history" Type string json:"type" Duration float64 json:"duration" RefundCredits bool json:"refund_credits" Stream bool json:"stream" ErrorType interface{} json:"error_type" ErrorMessage interface{} json:"error_message" } // Session sessionId type Session struct { Object string json:"object" ID string json:"id" }

2.獲取suno的token

實(shí)現(xiàn)邏輯為通過suno的cookie獲取到sessionid,后通過sessionid獲取token.

// GetToken getToken
func (s *service) GetToken(cookieString string) (token string, err error) {
var cookies []*http.Cookie
cookies = s.parseCookieString(cookieString)

jar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
sidUrl, _ := url.Parse(GetSidUrl)
jar.SetCookies(sidUrl, cookies)

client := &http.Client{
Jar: jar,
}

sidResp, err := client.Get(sidUrl.String())
if err != nil {
return "", err
}
defer sidResp.Body.Close()

var sidResponse SidResp
_ = json.NewDecoder(sidResp.Body).Decode(&sidResponse)

sid := ""
if len(sidResponse.Response.Sessions) > 0 {
sid = sidResponse.Response.Sessions[0].ID
fmt.Println(sidResponse.Response.Sessions[0].ID)
} else {
err = errors.New("獲取sessionId失敗:Response.Sessions為空")
return "", err
}

getTokenUrl := fmt.Sprintf("%s/%s/tokens?_clerk_js_version=4.72.0-snapshot.vc141245", GetTokenUrl, sid)
tokenResp, err := client.Post(getTokenUrl, ContentType, nil)
if err != nil {
return "", err
}
defer tokenResp.Body.Close()

var tokenResponse TokenResp
bodyBytes, err := io.ReadAll(tokenResp.Body)
if err != nil {
return "", fmt.Errorf("error reading response body: %v", err)
}

if tokenResp.StatusCode != http.StatusOK {
fmt.Printf("Error decoding JSON response: %v\nResponse body: %s\n", err, bodyBytes)
fmt.Printf("請(qǐng)求失敗,狀態(tài)碼: %d, 響應(yīng)內(nèi)容: %s\n", tokenResp.StatusCode, bodyBytes)
return "", errors.New(fmt.Sprintf("請(qǐng)求失敗,狀態(tài)碼: %d, 響應(yīng)內(nèi)容: %s\n", tokenResp.StatusCode, bodyBytes))
}

err = json.Unmarshal(bodyBytes, &tokenResponse)
if err != nil {
return "", err
}

if tokenResponse.Jwt == "" {
return "", errors.New("獲取token失敗:token為空")
}

return tokenResponse.Jwt, nil
}

// parseCookieString 格式化cookie
func (s *service) parseCookieString(cookieString string) []*http.Cookie {
var cookies []*http.Cookie
for _, cookiePair := range strings.Split(cookieString, "; ") {
parts := strings.Split(cookiePair, "=")
if len(parts) == 2 {
cookies = append(cookies, &http.Cookie{Name: parts[0], Value: parts[1]})
}
}
return cookies
}

3.生成歌曲提交

// Generate 發(fā)起生成請(qǐng)求
func (s *service) Generate(prompt string) (response GenerateResp, err error) {
// token校驗(yàn)
if err = s.checkToken(); err != nil {
return
}

// 攜帶token發(fā)起請(qǐng)求
genReq := GenerateReq{
GptDescriptionPrompt: prompt,
Mv: "chirp-v3-0",
}
reqData, _ := json.Marshal(genReq)
genMusicUrl := BaseUrl + "/api/generate/v2/"
req, _ := http.NewRequest("POST", genMusicUrl, bytes.NewBuffer(reqData))
req.Header.Set("Authorization", "Bearer "+Token)
req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return
}

defer resp.Body.Close()

var genResponse GenerateResp
err = json.NewDecoder(resp.Body).Decode(&genResponse)
if err != nil {
return
}

return genResponse, nil
}

4.查詢歌曲詳情

// GetFeed 查詢?cè)斍?br />
func (s *service) GetFeed(ids []string) (result []Clips, err error) {
// token校驗(yàn)
if err = s.checkToken(); err != nil {
return
}

idsFormat := url.PathEscape(strings.Join(ids, ","))
feedUrl := fmt.Sprintf("%s/api/feed/?ids=%s", BaseUrl, idsFormat)
req, _ := http.NewRequest("GET", feedUrl, nil)
req.Header.Set("Authorization", "Bearer "+Token)
req.Header.Set("Content-Type", "application/json")

client := &http.Client{
Timeout: 10 * time.Second,
}

resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()

bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body: %v", err)
}

err = json.Unmarshal(bodyBytes, &result)
if err != nil {
fmt.Printf("Error decoding JSON response: %v\nResponse body: %s\n", err, bodyBytes)
return nil, err
}
return result, nil
}

3.控制器

// GenMusic 提交音樂請(qǐng)求
func (h *Handler) GenMusic(c *gin.Context) {
var genReq genReq
if err := c.ShouldBindJSON(&genReq); err != nil {
errResponse(c, err.Error(), "Params Fail")
return
}

token, err := SunoService.GetToken(cookieString)
if err != nil {
errResponse(c, err.Error(), "Request Failed")
return
}

SunoService.SetToken(token)

res, err := SunoService.Generate(genReq.Prompt)
if err != nil {
errResponse(c, err.Error(), "Request Failed")
return
}

okResponse(c, res)
return
}

// GetFeed 查詢?cè)斍?br /> func (h *Handler) GetFeed(c *gin.Context) {
var feedReq feedReq
if err := c.ShouldBindJSON(&feedReq); err != nil {
errResponse(c, err.Error(), "Params Fail")
return
}

token, err := SunoService.GetToken(cookieString)
if err != nil {
errResponse(c, err.Error(), "Request Failed")
return
}
SunoService.SetToken(token)

data, err := SunoService.GetFeed(feedReq.ClipsIds)
if err != nil {
errResponse(c, err.Error(), "Request Failed")
return
}
okResponse(c, data)
return
}

postman測(cè)試

項(xiàng)目啟動(dòng)

cd cmd
go run main.go

生成音樂

查詢音樂詳情

結(jié)語(yǔ)

雖然目前這個(gè)suno-api僅揭開了其神秘的面紗,實(shí)現(xiàn)了基礎(chǔ)的生成及詳情查詢功能,但它所蘊(yùn)含的潛力與未來的發(fā)展空間卻是無限的。如果我們能夠?yàn)檫@個(gè)API增添更多的功能和優(yōu)化,它將變得更加強(qiáng)大和完善。比如,通過配置文件來管理cookie,讓它們更加安全和持久;增加cookie的保活機(jī)制,讓用戶體驗(yàn)更加流暢;記錄每一次的請(qǐng)求日志,讓問題追蹤和系統(tǒng)監(jiān)控變得更加簡(jiǎn)單;實(shí)施接口限流策略,保障系統(tǒng)在高并發(fā)情況下的穩(wěn)定性等等。希望后續(xù)能夠激發(fā)更多的創(chuàng)意火花,創(chuàng)造出更多動(dòng)人心弦的旋律。

感謝這段旅程有你的陪伴,讓我們一起期待下一次更加精彩的啟程。

文章轉(zhuǎn)自微信公眾號(hào)@AI創(chuàng)業(yè)之路

上一篇:

26步,把 Spotify 音樂集成進(jìn) SAP

下一篇:

一文搞懂如何使用ArcGIS API for Python訓(xùn)練深度學(xué)習(xí)模型
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊(cè)

多API并行試用

數(shù)據(jù)驅(qū)動(dòng)選型,提升決策效率

查看全部API→
??

熱門場(chǎng)景實(shí)測(cè),選對(duì)API

#AI文本生成大模型API

對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)

#AI深度推理大模型API

對(duì)比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)