Browse Source

!7 【增加】AI:我的音乐
Merge pull request !7 from 小新/master-jdk21-ai

芋道源码 11 months ago
parent
commit
2e9915b77b

+ 2 - 2
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicGenerateModeEnum.java

@@ -15,8 +15,8 @@ import java.util.Arrays;
 @Getter
 public enum AiMusicGenerateModeEnum implements IntArrayValuable {
 
-    LYRIC(1, "歌词模式"),
-    DESCRIPTION(2, "描述模式");
+    DESCRIPTION(1, "描述模式"),
+    LYRIC(2, "歌词模式");
 
     /**
      * 模式

+ 33 - 9
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.java

@@ -1,12 +1,10 @@
 package cn.iocoder.yudao.module.ai.controller.admin.music;
 
+import cn.hutool.core.util.ObjUtil;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicRespVO;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdatePublicStatusReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.music.vo.*;
 import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
 import cn.iocoder.yudao.module.ai.service.music.AiMusicService;
 import io.swagger.v3.oas.annotations.Operation;
@@ -30,13 +28,39 @@ public class AiMusicController {
     @Resource
     private AiMusicService musicService;
 
-    // TODO @xin:一个接口,获得【我的】音乐分页,参考 获得【我的】聊天角色分页 来写;用于我自己生成的列表,和音乐广场
+    @GetMapping("/my-page")
+    @Operation(summary = "获得【我的】音乐分页")
+    public CommonResult<PageResult<AiMusicRespVO>> getMusicMyPage(@Valid AiMusicPageReqVO pageReqVO) {
+        PageResult<AiMusicDO> pageResult = musicService.getMusicMyPage(pageReqVO, getLoginUserId());
+        return success(BeanUtils.toBean(pageResult, AiMusicRespVO.class));
+    }
 
-    // TODO @xin:一个接口,删除【我的】音乐
+    @Operation(summary = "删除【我的】音乐记录")
+    @DeleteMapping("/delete-my")
+    @Parameter(name = "id", required = true, description = "音乐编号", example = "1024")
+    public CommonResult<Boolean> deleteMusicMy(@RequestParam("id") Long id) {
+        musicService.deleteMusicMy(id, getLoginUserId());
+        return success(true);
+    }
 
-    // TODO @xin:一个接口,获得【我的】音乐
+    @GetMapping("/get-my")
+    @Operation(summary = "获取【我的】音乐")
+    @Parameter(name = "id", required = true, description = "音乐编号", example = "1024")
+    public CommonResult<AiMusicRespVO> getMusicMy(@RequestParam("id") Long id) {
+        AiMusicDO music = musicService.getMusic(id);
+        if (music == null || ObjUtil.notEqual(getLoginUserId(), music.getUserId())) {
+            return success(null);
+        }
+        return success(BeanUtils.toBean(music, AiMusicRespVO.class));
+    }
 
-    // TODO @xin:一个接口,修改【我的】音乐,目前只支持修改标题
+    @PostMapping("/updateTitle-my")
+    @Operation(summary = "修改【我的】音乐 目前只支持修改标题")
+    @Parameter(name = "title", required = true, description = "音乐名称", example = "夜空中最亮的星")
+    public CommonResult<Boolean> updateMusicTitle(AiMusicUpdateTitleReqVO updateReqVO) {
+        musicService.updateMusicTitle(updateReqVO);
+        return success(true);
+    }
 
     @PostMapping("/generate")
     @Operation(summary = "音乐生成")
@@ -44,7 +68,7 @@ public class AiMusicController {
         return success(musicService.generateMusic(getLoginUserId(), reqVO));
     }
 
-    // ================ 绘图管理 ================
+    // ================ 音乐管理 ================
 
     @GetMapping("/page")
     @Operation(summary = "获得音乐分页")

+ 3 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicRespVO.java

@@ -61,6 +61,9 @@ public class AiMusicRespVO {
     @Schema(description = "错误信息")
     private String errorMessage;
 
+    @Schema(description = "音乐时长")
+    private Double duration;
+
     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime createTime;
 

+ 18 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateTitleReqVO.java

@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "管理后台 - AI 音乐修改名称 Request VO")
+@Data
+public class AiMusicUpdateTitleReqVO {
+
+    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
+    private Long id;
+
+    @Schema(description = "音乐名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "夜空中最亮的星")
+    @NotNull(message = "音乐名称不能为空")
+    private String title;
+
+}

+ 22 - 8
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiSunoGenerateReqVO.java

@@ -17,24 +17,38 @@ public class AiSunoGenerateReqVO {
     private String platform; // 参见 AiPlatformEnum 枚举
 
     /**
-     * 1. 描述模式:描述词 + 是否纯音乐 + 模型 TODO @xin:目前貌似描述词没弄对?看着不是 prompt 字段(也可能我弄错了)。可以微信再沟通下哈
-     * 2. 歌词模式:歌词 + 音乐风格 + 标题 + 模型 TODO @xin:目前这块少传递了标题;
+     * 1. 描述模式:描述词 + 是否纯音乐 + 模型
+     * 2. 歌词模式:歌词 + 音乐风格 + 标题 + 模型
      */
-    @Schema(description = "生成模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
+    @Schema(description = "生成模式 1.描述模式 2. 歌词模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
     @NotNull(message = "生成模式不能为空")
     private Integer generateMode; // 参见 AiMusicGenerateModeEnum 枚举
 
-    @Schema(description = "用于生成音乐音频的提示", requiredMode = Schema.RequiredMode.REQUIRED,
-            example = "创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")
+    @Schema(description = "歌词模式用:用于生成音乐音频的歌词提示", requiredMode = Schema.RequiredMode.NOT_REQUIRED,
+            example = """
+                    [Verse]
+                    阳光下奔跑 多么欢快
+                    假期就要来 心都飞起来
+                    朋友在一旁 笑声又灿烂
+                    无忧无虑的 每一天甜蜜
+                    [Chorus]
+                    马上放假了 快来庆祝
+                    一起去旅行 快去冒险
+                    日子太短暂 别再等待
+                    马上放假了 梦想起飞
+                    """)
     private String prompt;
 
+    @Schema(description = "描述模式用:用于生成音乐音频的描述", requiredMode = Schema.RequiredMode.NOT_REQUIRED,
+            example = "创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")
+    private String gptDescriptionPrompt;
+
     @Schema(description = "是否纯音乐", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "true")
     private Boolean makeInstrumental;
 
-    // TODO @xin:看了下这个字段,发现最终还是 model 合适点;因为它其实是模型
-    @Schema(description = "模型版本", requiredMode = Schema.RequiredMode.REQUIRED, example = "chirp-v3.5")
+    @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "chirp-v3.5")
     @NotEmpty(message = "模型不能为空")
-    private String modelVersion; // 参见 AiModelEnum 枚举
+    private String model; // 参见 AiModelEnum 枚举
 
     @Schema(description = "音乐风格", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "[\"pop\",\"jazz\",\"punk\"]")
     private List<String> tags;

+ 8 - 2
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java

@@ -30,7 +30,7 @@ public class AiMusicDO extends BaseDO {
 
     /**
      * 用户编号
-     *
+     * <p>
      * 关联 AdminUserDO 的 userId 字段
      */
     private Long userId;
@@ -67,7 +67,7 @@ public class AiMusicDO extends BaseDO {
 
     /**
      * 生成模式
-     *
+     * <p>
      * 枚举 {@link AiMusicGenerateModeEnum}
      */
     private Integer generateMode;
@@ -113,4 +113,10 @@ public class AiMusicDO extends BaseDO {
      */
     private String errorMessage;
 
+
+    /**
+     * 音乐时长
+     */
+    private Double duration;
+
 }

+ 9 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/music/AiMusicMapper.java

@@ -32,4 +32,13 @@ public interface AiMusicMapper extends BaseMapperX<AiMusicDO> {
                 .orderByDesc(AiMusicDO::getId));
     }
 
+    default PageResult<AiMusicDO> selectPageByMy(AiMusicPageReqVO reqVO, Long userId) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<AiMusicDO>()
+                // 情况一:公开
+                .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiMusicDO::getPublicStatus, reqVO.getPublicStatus())
+                // 情况二:私有
+                .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiMusicDO::getUserId, userId)
+                .orderByAsc(AiMusicDO::getId));
+    }
+    
 }

+ 33 - 4
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicService.java

@@ -1,9 +1,7 @@
 package cn.iocoder.yudao.module.ai.service.music;
 
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdatePublicStatusReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.music.vo.*;
 import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
 import jakarta.validation.Valid;
 
@@ -20,7 +18,7 @@ public interface AiMusicService {
      * 音乐生成
      *
      * @param userId 用户编号
-     * @param reqVO 请求参数
+     * @param reqVO  请求参数
      * @return 生成的音乐ID
      */
     List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO);
@@ -39,6 +37,13 @@ public interface AiMusicService {
      */
     void updateMusicPublicStatus(@Valid AiMusicUpdatePublicStatusReqVO updateReqVO);
 
+    /**
+     * 更新音乐名称
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateMusicTitle(@Valid AiMusicUpdateTitleReqVO updateReqVO);
+
     /**
      * 删除AI 音乐
      *
@@ -46,6 +51,22 @@ public interface AiMusicService {
      */
     void deleteMusic(Long id);
 
+    /**
+     * 删除【我的】音乐记录
+     *
+     * @param id     音乐编号
+     * @param userId 用户编号
+     */
+    void deleteMusicMy(Long id, Long userId);
+
+    /**
+     * 获得AI 音乐
+     *
+     * @param id 音乐编号
+     * @return 音乐内容
+     */
+    AiMusicDO getMusic(Long id);
+
     /**
      * 获得音乐分页
      *
@@ -54,4 +75,12 @@ public interface AiMusicService {
      */
     PageResult<AiMusicDO> getMusicPage(AiMusicPageReqVO pageReqVO);
 
+    /**
+     * 获得【我的】音乐分页
+     *
+     * @param pageReqVO 分页查询
+     * @param userId    用户编号
+     * @return 音乐分页
+     */
+    PageResult<AiMusicDO> getMusicMyPage(AiMusicPageReqVO pageReqVO, Long userId);
 }

+ 80 - 25
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java

@@ -2,16 +2,17 @@ package cn.iocoder.yudao.module.ai.service.music;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.text.StrPool;
+import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpUtil;
 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdatePublicStatusReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.music.vo.*;
 import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
 import cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper;
 import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;
 import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;
+import cn.iocoder.yudao.module.infra.api.file.FileApi;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
@@ -21,6 +22,7 @@ import java.util.*;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
+import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.IMAGE_NOT_EXISTS;
 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MUSIC_NOT_EXISTS;
 
 /**
@@ -38,6 +40,9 @@ public class AiMusicServiceImpl implements AiMusicService {
     @Resource
     private AiMusicMapper musicMapper;
 
+    @Resource
+    private FileApi fileApi;
+
     @Override
     public List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO) {
         // 1. 调用 Suno 生成音乐
@@ -45,12 +50,12 @@ public class AiMusicServiceImpl implements AiMusicService {
         if (Objects.equals(AiMusicGenerateModeEnum.LYRIC.getMode(), reqVO.getGenerateMode())) {
             // 1.1 歌词模式
             SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
-                    reqVO.getPrompt(), reqVO.getModelVersion(), CollUtil.join(reqVO.getTags(), StrPool.COMMA), reqVO.getTitle());
+                    reqVO.getPrompt(), reqVO.getModel(), CollUtil.join(reqVO.getTags(), StrPool.COMMA), reqVO.getTitle());
             musicDataList = sunoApi.customGenerate(generateRequest);
         } else if (Objects.equals(AiMusicGenerateModeEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) {
             // 1.2 描述模式
             SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
-                    reqVO.getPrompt(), reqVO.getModelVersion(), reqVO.getMakeInstrumental());
+                    reqVO.getGptDescriptionPrompt(), reqVO.getModel(), reqVO.getMakeInstrumental());
             musicDataList = sunoApi.generate(generateRequest);
         } else {
             throw new IllegalArgumentException(StrUtil.format("未知生成模式({})", reqVO));
@@ -90,23 +95,6 @@ public class AiMusicServiceImpl implements AiMusicService {
         return streamingTask.size();
     }
 
-    /**
-     * 构建 AiMusicDO 集合
-     *
-     * @param musicList suno 音乐任务列表
-     * @return AiMusicDO 集合
-     */
-    private static List<AiMusicDO> buildMusicDOList(List<SunoApi.MusicData> musicList) {
-        // TODO @xin:成功的情况下,需要下载到自己的文件服务器。参考图片的处理
-        return convertList(musicList, musicData -> new AiMusicDO()
-                .setTaskId(musicData.id()).setModel(musicData.modelName())
-                .setPrompt(musicData.prompt()).setGptDescriptionPrompt(musicData.gptDescriptionPrompt())
-                .setAudioUrl(musicData.audioUrl()).setVideoUrl(musicData.videoUrl()).setImageUrl(musicData.imageUrl())
-                .setTitle(musicData.title()).setLyric(musicData.lyric()).setTags(StrUtil.split(musicData.tags(), StrPool.COMMA))
-                .setStatus(Objects.equals("complete", musicData.status()) ?
-                        AiMusicStatusEnum.SUCCESS.getStatus() : AiMusicStatusEnum.IN_PROGRESS.getStatus()));
-    }
-
     @Override
     public void updateMusicPublicStatus(AiMusicUpdatePublicStatusReqVO updateReqVO) {
         // 校验存在
@@ -115,6 +103,14 @@ public class AiMusicServiceImpl implements AiMusicService {
         musicMapper.updateById(new AiMusicDO().setId(updateReqVO.getId()).setPublicStatus(updateReqVO.getPublicStatus()));
     }
 
+    @Override
+    public void updateMusicTitle(AiMusicUpdateTitleReqVO updateReqVO) {
+        // 校验存在
+        validateMusicExists(updateReqVO.getId());
+        // 更新
+        musicMapper.updateById(new AiMusicDO().setId(updateReqVO.getId()).setTitle(updateReqVO.getTitle()));
+    }
+
     @Override
     public void deleteMusic(Long id) {
         // 校验存在
@@ -123,10 +119,20 @@ public class AiMusicServiceImpl implements AiMusicService {
         musicMapper.deleteById(id);
     }
 
-    private void validateMusicExists(Long id) {
-        if (musicMapper.selectById(id) == null) {
-            throw exception(MUSIC_NOT_EXISTS);
+    @Override
+    public void deleteMusicMy(Long id, Long userId) {
+        // 1. 校验是否存在
+        AiMusicDO music = validateMusicExists(id);
+        if (ObjUtil.notEqual(music.getUserId(), userId)) {
+            throw exception(IMAGE_NOT_EXISTS);
         }
+        // 2. 删除记录
+        musicMapper.deleteById(id);
+    }
+
+    @Override
+    public AiMusicDO getMusic(Long id) {
+        return musicMapper.selectById(id);
     }
 
     @Override
@@ -134,4 +140,53 @@ public class AiMusicServiceImpl implements AiMusicService {
         return musicMapper.selectPage(pageReqVO);
     }
 
+    @Override
+    public PageResult<AiMusicDO> getMusicMyPage(AiMusicPageReqVO pageReqVO, Long userId) {
+        return musicMapper.selectPageByMy(pageReqVO, userId);
+    }
+
+    /**
+     * 构建 AiMusicDO 集合
+     *
+     * @param musicList suno 音乐任务列表
+     * @return AiMusicDO 集合
+     */
+    private List<AiMusicDO> buildMusicDOList(List<SunoApi.MusicData> musicList) {
+        return convertList(musicList, musicData -> new AiMusicDO()
+                .setTaskId(musicData.id()).setModel(musicData.modelName())
+                .setPrompt(musicData.prompt()).setGptDescriptionPrompt(musicData.gptDescriptionPrompt())
+                .setAudioUrl(createFile(musicData.audioUrl())).setVideoUrl(createFile(musicData.videoUrl())).setImageUrl(createFile(musicData.imageUrl())).setDuration(musicData.duration())
+                .setTitle(musicData.title()).setLyric(musicData.lyric()).setTags(StrUtil.split(musicData.tags(), StrPool.COMMA))
+                .setStatus(Objects.equals("complete", musicData.status()) ?
+                        AiMusicStatusEnum.SUCCESS.getStatus() : AiMusicStatusEnum.IN_PROGRESS.getStatus()));
+    }
+
+    /**
+     * 将生成的音频文件上传到文件服务器
+     *
+     * @param url 音频文件地址
+     * @return 内部文件地址
+     */
+    private String createFile(String url) {
+        if (StrUtil.isBlank(url)) {
+            return null;
+        }
+        byte[] bytes = HttpUtil.downloadBytes(url);
+        return fileApi.createFile(bytes);
+    }
+
+    /**
+     * 校验音乐是否存在
+     *
+     * @param id 音乐编号
+     * @return 音乐信息
+     */
+    private AiMusicDO validateMusicExists(Long id) {
+        AiMusicDO music = musicMapper.selectById(id);
+        if (music == null) {
+            throw exception(MUSIC_NOT_EXISTS);
+        }
+        return music;
+    }
+
 }

+ 9 - 7
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/suno/api/SunoApi.java

@@ -117,7 +117,7 @@ public class SunoApi {
      * @param prompt           用于生成音乐音频的提示
      * @param tags             音乐风格
      * @param title            音乐名称
-     * @param mv               模型
+     * @param model            模型
      * @param waitAudio        false 表示后台模式,仅返回音频任务信息,需要调用 get API 获取详细的音频信息。
      *                         true 表示同步模式,API 最多等待 100s,音频生成完毕后直接返回音频链接等信息,建议在 GPT 等 agent 中使用。
      * @param makeInstrumental 指示音乐音频是否为定制,如果为 true,则从歌词生成,否则从提示生成
@@ -127,7 +127,7 @@ public class SunoApi {
             String prompt,
             String tags,
             String title,
-            String mv,
+            String model,
             @JsonProperty("wait_audio") boolean waitAudio,
             @JsonProperty("make_instrumental") boolean makeInstrumental
     ) {
@@ -136,12 +136,12 @@ public class SunoApi {
             this(prompt, null, null, null, false, false);
         }
 
-        public MusicGenerateRequest(String prompt, String mv, boolean makeInstrumental) {
-            this(prompt, null, null, mv, false, makeInstrumental);
+        public MusicGenerateRequest(String prompt, String model, boolean makeInstrumental) {
+            this(prompt, null, null, model, false, makeInstrumental);
         }
 
-        public MusicGenerateRequest(String prompt, String mv, String tags, String title) {
-            this(prompt, tags, title, mv, false, false);
+        public MusicGenerateRequest(String prompt, String model, String tags, String title) {
+            this(prompt, tags, title, model, false, false);
         }
 
     }
@@ -162,6 +162,7 @@ public class SunoApi {
      * @param prompt               生成音乐音频的提示
      * @param type                 操作类型
      * @param tags                 音乐类型标签
+     * @param duration             音乐时长
      */
     public record MusicData(
             String id,
@@ -176,7 +177,8 @@ public class SunoApi {
             @JsonProperty("gpt_description_prompt") String gptDescriptionPrompt,
             String prompt,
             String type,
-            String tags
+            String tags,
+            Double duration
     ) {
     }
 

+ 2 - 2
yudao-server/src/main/resources/application.yaml

@@ -201,8 +201,8 @@ yudao.ai:
     notify-url: http://java.nat300.top/admin-api/ai/image/midjourney/notify
   suno:
     enable: true
-#    base-url: https://suno-om0w1cy6e-status2xxs-projects.vercel.app
-    base-url: http://127.0.0.1:3001
+    base-url: https://suno-55ishh05u-status2xxs-projects.vercel.app
+#    base-url: http://127.0.0.1:3001
 
 --- #################### 芋道相关配置 ####################