简单实现Ai音乐suno-api

本文由 ChatMoney团队出品

前言

在科技与艺术的交汇处,AI音乐创作正以其独特的魅力,引领着音乐产业的一次革命。不久前,AI音乐的浪潮席卷了整个创意领域,激发了无数音乐爱好者和技术开发者的无限想象。在这场音乐与科技的盛宴中,主流的AI音乐平台suno无疑成为了焦点,尽管它尚未对外开放API服务,但这并未阻止我们探索的脚步。

今天,我们将踏上一段奇妙的旅程,用Go语言这把精准而强大的工具,尝试构建一个简易的suno-api。

开发前准备

  1. golang开发环境。
  2. golang版本:1.21.0
  3. 可登录suno的环境
  4. 获取suno平台cookie
简单实现Ai音乐suno-api

开发过程

1.安装gin框架

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

2.封装suno请求

  1. 相关结构体
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"`
}
  1. 获取suno的token
  2. 实现逻辑为通过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: %vnResponse body: %sn", err, bodyBytes)
      fmt.Printf("请求失败,状态码: %d, 响应内容: %sn", tokenResp.StatusCode, bodyBytes)
      return "", errors.New(fmt.Sprintf("请求失败,状态码: %d, 响应内容: %sn", 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
}
  1. 生成歌曲提交
// Generate 发起生成请求
func (s *service) Generate(prompt string) (response GenerateResp, err error) {
   // token校验
   if err = s.checkToken(); err != nil {
      return
   }

   // 携带token发起请求
   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
}
  1. 查询歌曲详情
// GetFeed 查询详情
func (s *service) GetFeed(ids []string) (result []Clips, err error) {
   // token校验
   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: %vnResponse body: %sn", err, bodyBytes)
      return nil, err
   }
   return result, nil
}

3.控制器

// GenMusic 提交音乐请求
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 查询详情
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测试

项目启动

cd cmd
go run main.go

生成音乐

简单实现Ai音乐suno-api

查询音乐详情

简单实现Ai音乐suno-api

结语

虽然目前这个suno-api仅揭开了其神秘的面纱,实现了基础的生成及详情查询功能,但它所蕴含的潜力与未来的发展空间却是无限的。如果我们能够为这个API增添更多的功能和优化,它将变得更加强大和完善。比如,通过配置文件来管理cookie,让它们更加安全和持久;增加cookie的保活机制,让用户体验更加流畅;记录每一次的请求日志,让问题追踪和系统监控变得更加简单;实施接口限流策略,保障系统在高并发情况下的稳定性等等。希望后续能够激发更多的创意火花,创造出更多动人心弦的旋律。

感谢这段旅程有你的陪伴,让我们一起期待下一次更加精彩的启程。

完整代码

https://gitee.com/mofung1/go-suno

关于我们

本文由ChatMoney团队出品,ChatMoney专注于AI应用落地与变现,我们提供全套、持续更新的AI源码系统与可执行的变现方案,致力于帮助更多人利用AI来变现,欢迎进入ChatMoney获取更多AI变现方案!

ChatMoney的头像ChatMoney
Previous 2024年 6月 28日 上午10:36
Next 2024年 7月 2日 下午2:47

相关推荐

  • 网页文件加载失败如何重试

    本文由 ChatMoney团队出品 在我们开发网站应用时,我们可能会遇到脚本加载失败的情况,导致脚本加载失败的原因有很多,比如用户的网络问题、终端设备问题、用户浏览器版本等诸多因素。 解决方案 在 JavaScript 中,我们可以创建一个监听来监听脚本加载失败的情况,然后针对加载失败的脚本进行重新加载。 重新加载的方案,一般是通过更换域名来解决。我们给每个…

    2024年 7月 16日
    150
  • Android Studio下载Gradle超时解决方案

    本文由 ChatMoney团队出品 Android Studio 找到项目中 gradle 配置文件的路径,我的路径为 /你的项目路径/gradle/wrapper/gradle-wrapper.properties,里面对应内容应该如下所示: 超时原因是因为as(Android Studio,此后简称 as)中默认是从gradle官网去下载,此时我们有一个…

    2024年 7月 17日
    195
  • 你写的深度克隆真的“深度”吗?

    本文由 ChatMoney团队出品 深度克隆是前端开发中无法避免的话题,几乎每个前端开发者都遇到过这个话题,那我们就来看看你写的深度克隆真的正确吗? 大家先看下面这段代码: 平时开发中用这个方法或者过去用过这个方法去“深度克隆”的同学请举手🙋,我相信应该不在少数。也不是说这个方法是错的,它其实在绝大多数场景都能用,但是在一些复杂场景就会有问题,比如下面这几个…

    2024年 8月 1日
    208
  • GPT-4o:人工智能的新突破

    本文由 ChatMoney团队出品 一、引言 GPT-4o 是 OpenAI 发布的最新人工智能模型,它在 GPT-4 的基础上进行了升级,具备更强大的语言理解和生成能力。本文将详细介绍 GPT-4o 的发布内容、技术实现难度以及未来发展方向,特别是在人情感方向的探索。 二、GPT-4o 的发布内容 多模态交互:GPT-4o 能够同时处理文本、图像和音频输入…

    2024年 6月 17日
    209
  • 浅谈工厂模式

    本文由 ChatMoney团队出品 掌握优秀的设计模式,可以显著提高我们的技术能力。此外,它还能让我们编写的代码更加优雅,减少错误,并更好地扩展和维护项目。 基础知识 简单工厂模式 简单工厂模式(Simple Factory Pattern)指的是一个工厂类根据传入的参数来决定创建哪一种产品类的实例。尽管这种模式简单,但它违反了开放封闭原则,因为每增加一个新…

    2024年 7月 23日
    214

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信