Commit 2e9915b77baef6b151fe5edbf0dbfdb19ce77b57

Authored by 芋道源码
Committed by Gitee
2 parents 2c61acf9 94460008

!7 【增加】AI:我的音乐

Merge pull request !7 from 小新/master-jdk21-ai
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;
15 15 @Getter
16 16 public enum AiMusicGenerateModeEnum implements IntArrayValuable {
17 17  
18   - LYRIC(1, "歌词模式"),
19   - DESCRIPTION(2, "描述模式");
  18 + DESCRIPTION(1, "描述模式"),
  19 + LYRIC(2, "歌词模式");
20 20  
21 21 /**
22 22 * 模式
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.music;
2 2  
  3 +import cn.hutool.core.util.ObjUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6   -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
7   -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicRespVO;
8   -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdatePublicStatusReqVO;
9   -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
  7 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.*;
10 8 import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
11 9 import cn.iocoder.yudao.module.ai.service.music.AiMusicService;
12 10 import io.swagger.v3.oas.annotations.Operation;
... ... @@ -30,13 +28,39 @@ public class AiMusicController {
30 28 @Resource
31 29 private AiMusicService musicService;
32 30  
33   - // TODO @xin:一个接口,获得【我的】音乐分页,参考 获得【我的】聊天角色分页 来写;用于我自己生成的列表,和音乐广场
  31 + @GetMapping("/my-page")
  32 + @Operation(summary = "获得【我的】音乐分页")
  33 + public CommonResult<PageResult<AiMusicRespVO>> getMusicMyPage(@Valid AiMusicPageReqVO pageReqVO) {
  34 + PageResult<AiMusicDO> pageResult = musicService.getMusicMyPage(pageReqVO, getLoginUserId());
  35 + return success(BeanUtils.toBean(pageResult, AiMusicRespVO.class));
  36 + }
34 37  
35   - // TODO @xin:一个接口,删除【我的】音乐
  38 + @Operation(summary = "删除【我的】音乐记录")
  39 + @DeleteMapping("/delete-my")
  40 + @Parameter(name = "id", required = true, description = "音乐编号", example = "1024")
  41 + public CommonResult<Boolean> deleteMusicMy(@RequestParam("id") Long id) {
  42 + musicService.deleteMusicMy(id, getLoginUserId());
  43 + return success(true);
  44 + }
36 45  
37   - // TODO @xin:一个接口,获得【我的】音乐
  46 + @GetMapping("/get-my")
  47 + @Operation(summary = "获取【我的】音乐")
  48 + @Parameter(name = "id", required = true, description = "音乐编号", example = "1024")
  49 + public CommonResult<AiMusicRespVO> getMusicMy(@RequestParam("id") Long id) {
  50 + AiMusicDO music = musicService.getMusic(id);
  51 + if (music == null || ObjUtil.notEqual(getLoginUserId(), music.getUserId())) {
  52 + return success(null);
  53 + }
  54 + return success(BeanUtils.toBean(music, AiMusicRespVO.class));
  55 + }
38 56  
39   - // TODO @xin:一个接口,修改【我的】音乐,目前只支持修改标题
  57 + @PostMapping("/updateTitle-my")
  58 + @Operation(summary = "修改【我的】音乐 目前只支持修改标题")
  59 + @Parameter(name = "title", required = true, description = "音乐名称", example = "夜空中最亮的星")
  60 + public CommonResult<Boolean> updateMusicTitle(AiMusicUpdateTitleReqVO updateReqVO) {
  61 + musicService.updateMusicTitle(updateReqVO);
  62 + return success(true);
  63 + }
40 64  
41 65 @PostMapping("/generate")
42 66 @Operation(summary = "音乐生成")
... ... @@ -44,7 +68,7 @@ public class AiMusicController {
44 68 return success(musicService.generateMusic(getLoginUserId(), reqVO));
45 69 }
46 70  
47   - // ================ 绘图管理 ================
  71 + // ================ 音乐管理 ================
48 72  
49 73 @GetMapping("/page")
50 74 @Operation(summary = "获得音乐分页")
... ...
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 {
61 61 @Schema(description = "错误信息")
62 62 private String errorMessage;
63 63  
  64 + @Schema(description = "音乐时长")
  65 + private Double duration;
  66 +
64 67 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
65 68 private LocalDateTime createTime;
66 69  
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateTitleReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import jakarta.validation.constraints.NotNull;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - AI 音乐修改名称 Request VO")
  8 +@Data
  9 +public class AiMusicUpdateTitleReqVO {
  10 +
  11 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
  12 + private Long id;
  13 +
  14 + @Schema(description = "音乐名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "夜空中最亮的星")
  15 + @NotNull(message = "音乐名称不能为空")
  16 + private String title;
  17 +
  18 +}
0 19 \ No newline at end of file
... ...
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 {
17 17 private String platform; // 参见 AiPlatformEnum 枚举
18 18  
19 19 /**
20   - * 1. 描述模式:描述词 + 是否纯音乐 + 模型 TODO @xin:目前貌似描述词没弄对?看着不是 prompt 字段(也可能我弄错了)。可以微信再沟通下哈
21   - * 2. 歌词模式:歌词 + 音乐风格 + 标题 + 模型 TODO @xin:目前这块少传递了标题;
  20 + * 1. 描述模式:描述词 + 是否纯音乐 + 模型
  21 + * 2. 歌词模式:歌词 + 音乐风格 + 标题 + 模型
22 22 */
23   - @Schema(description = "生成模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  23 + @Schema(description = "生成模式 1.描述模式 2. 歌词模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
24 24 @NotNull(message = "生成模式不能为空")
25 25 private Integer generateMode; // 参见 AiMusicGenerateModeEnum 枚举
26 26  
27   - @Schema(description = "用于生成音乐音频的提示", requiredMode = Schema.RequiredMode.REQUIRED,
28   - example = "创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")
  27 + @Schema(description = "歌词模式用:用于生成音乐音频的歌词提示", requiredMode = Schema.RequiredMode.NOT_REQUIRED,
  28 + example = """
  29 + [Verse]
  30 + 阳光下奔跑 多么欢快
  31 + 假期就要来 心都飞起来
  32 + 朋友在一旁 笑声又灿烂
  33 + 无忧无虑的 每一天甜蜜
  34 + [Chorus]
  35 + 马上放假了 快来庆祝
  36 + 一起去旅行 快去冒险
  37 + 日子太短暂 别再等待
  38 + 马上放假了 梦想起飞
  39 + """)
29 40 private String prompt;
30 41  
  42 + @Schema(description = "描述模式用:用于生成音乐音频的描述", requiredMode = Schema.RequiredMode.NOT_REQUIRED,
  43 + example = "创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")
  44 + private String gptDescriptionPrompt;
  45 +
31 46 @Schema(description = "是否纯音乐", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "true")
32 47 private Boolean makeInstrumental;
33 48  
34   - // TODO @xin:看了下这个字段,发现最终还是 model 合适点;因为它其实是模型
35   - @Schema(description = "模型版本", requiredMode = Schema.RequiredMode.REQUIRED, example = "chirp-v3.5")
  49 + @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "chirp-v3.5")
36 50 @NotEmpty(message = "模型不能为空")
37   - private String modelVersion; // 参见 AiModelEnum 枚举
  51 + private String model; // 参见 AiModelEnum 枚举
38 52  
39 53 @Schema(description = "音乐风格", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "[\"pop\",\"jazz\",\"punk\"]")
40 54 private List<String> tags;
... ...
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 {
30 30  
31 31 /**
32 32 * 用户编号
33   - *
  33 + * <p>
34 34 * 关联 AdminUserDO 的 userId 字段
35 35 */
36 36 private Long userId;
... ... @@ -67,7 +67,7 @@ public class AiMusicDO extends BaseDO {
67 67  
68 68 /**
69 69 * 生成模式
70   - *
  70 + * <p>
71 71 * 枚举 {@link AiMusicGenerateModeEnum}
72 72 */
73 73 private Integer generateMode;
... ... @@ -113,4 +113,10 @@ public class AiMusicDO extends BaseDO {
113 113 */
114 114 private String errorMessage;
115 115  
  116 +
  117 + /**
  118 + * 音乐时长
  119 + */
  120 + private Double duration;
  121 +
116 122 }
... ...
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&lt;AiMusicDO&gt; {
32 32 .orderByDesc(AiMusicDO::getId));
33 33 }
34 34  
  35 + default PageResult<AiMusicDO> selectPageByMy(AiMusicPageReqVO reqVO, Long userId) {
  36 + return selectPage(reqVO, new LambdaQueryWrapperX<AiMusicDO>()
  37 + // 情况一:公开
  38 + .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiMusicDO::getPublicStatus, reqVO.getPublicStatus())
  39 + // 情况二:私有
  40 + .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiMusicDO::getUserId, userId)
  41 + .orderByAsc(AiMusicDO::getId));
  42 + }
  43 +
35 44 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicService.java
1 1 package cn.iocoder.yudao.module.ai.service.music;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4   -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
5   -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdatePublicStatusReqVO;
6   -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
  4 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.*;
7 5 import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
8 6 import jakarta.validation.Valid;
9 7  
... ... @@ -20,7 +18,7 @@ public interface AiMusicService {
20 18 * 音乐生成
21 19 *
22 20 * @param userId 用户编号
23   - * @param reqVO 请求参数
  21 + * @param reqVO 请求参数
24 22 * @return 生成的音乐ID
25 23 */
26 24 List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO);
... ... @@ -40,6 +38,13 @@ public interface AiMusicService {
40 38 void updateMusicPublicStatus(@Valid AiMusicUpdatePublicStatusReqVO updateReqVO);
41 39  
42 40 /**
  41 + * 更新音乐名称
  42 + *
  43 + * @param updateReqVO 更新信息
  44 + */
  45 + void updateMusicTitle(@Valid AiMusicUpdateTitleReqVO updateReqVO);
  46 +
  47 + /**
43 48 * 删除AI 音乐
44 49 *
45 50 * @param id 编号
... ... @@ -47,6 +52,22 @@ public interface AiMusicService {
47 52 void deleteMusic(Long id);
48 53  
49 54 /**
  55 + * 删除【我的】音乐记录
  56 + *
  57 + * @param id 音乐编号
  58 + * @param userId 用户编号
  59 + */
  60 + void deleteMusicMy(Long id, Long userId);
  61 +
  62 + /**
  63 + * 获得AI 音乐
  64 + *
  65 + * @param id 音乐编号
  66 + * @return 音乐内容
  67 + */
  68 + AiMusicDO getMusic(Long id);
  69 +
  70 + /**
50 71 * 获得音乐分页
51 72 *
52 73 * @param pageReqVO 分页查询
... ... @@ -54,4 +75,12 @@ public interface AiMusicService {
54 75 */
55 76 PageResult<AiMusicDO> getMusicPage(AiMusicPageReqVO pageReqVO);
56 77  
  78 + /**
  79 + * 获得【我的】音乐分页
  80 + *
  81 + * @param pageReqVO 分页查询
  82 + * @param userId 用户编号
  83 + * @return 音乐分页
  84 + */
  85 + PageResult<AiMusicDO> getMusicMyPage(AiMusicPageReqVO pageReqVO, Long userId);
57 86 }
... ...
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;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.text.StrPool;
  5 +import cn.hutool.core.util.ObjUtil;
5 6 import cn.hutool.core.util.StrUtil;
  7 +import cn.hutool.http.HttpUtil;
6 8 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
7 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8   -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
9   -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdatePublicStatusReqVO;
10   -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
  10 +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.*;
11 11 import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
12 12 import cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper;
13 13 import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;
14 14 import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;
  15 +import cn.iocoder.yudao.module.infra.api.file.FileApi;
15 16 import jakarta.annotation.Resource;
16 17 import lombok.extern.slf4j.Slf4j;
17 18 import org.springframework.stereotype.Service;
... ... @@ -21,6 +22,7 @@ import java.util.*;
21 22 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
22 23 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
23 24 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  25 +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.IMAGE_NOT_EXISTS;
24 26 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MUSIC_NOT_EXISTS;
25 27  
26 28 /**
... ... @@ -38,6 +40,9 @@ public class AiMusicServiceImpl implements AiMusicService {
38 40 @Resource
39 41 private AiMusicMapper musicMapper;
40 42  
  43 + @Resource
  44 + private FileApi fileApi;
  45 +
41 46 @Override
42 47 public List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO) {
43 48 // 1. 调用 Suno 生成音乐
... ... @@ -45,12 +50,12 @@ public class AiMusicServiceImpl implements AiMusicService {
45 50 if (Objects.equals(AiMusicGenerateModeEnum.LYRIC.getMode(), reqVO.getGenerateMode())) {
46 51 // 1.1 歌词模式
47 52 SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
48   - reqVO.getPrompt(), reqVO.getModelVersion(), CollUtil.join(reqVO.getTags(), StrPool.COMMA), reqVO.getTitle());
  53 + reqVO.getPrompt(), reqVO.getModel(), CollUtil.join(reqVO.getTags(), StrPool.COMMA), reqVO.getTitle());
49 54 musicDataList = sunoApi.customGenerate(generateRequest);
50 55 } else if (Objects.equals(AiMusicGenerateModeEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) {
51 56 // 1.2 描述模式
52 57 SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
53   - reqVO.getPrompt(), reqVO.getModelVersion(), reqVO.getMakeInstrumental());
  58 + reqVO.getGptDescriptionPrompt(), reqVO.getModel(), reqVO.getMakeInstrumental());
54 59 musicDataList = sunoApi.generate(generateRequest);
55 60 } else {
56 61 throw new IllegalArgumentException(StrUtil.format("未知生成模式({})", reqVO));
... ... @@ -90,23 +95,6 @@ public class AiMusicServiceImpl implements AiMusicService {
90 95 return streamingTask.size();
91 96 }
92 97  
93   - /**
94   - * 构建 AiMusicDO 集合
95   - *
96   - * @param musicList suno 音乐任务列表
97   - * @return AiMusicDO 集合
98   - */
99   - private static List<AiMusicDO> buildMusicDOList(List<SunoApi.MusicData> musicList) {
100   - // TODO @xin:成功的情况下,需要下载到自己的文件服务器。参考图片的处理
101   - return convertList(musicList, musicData -> new AiMusicDO()
102   - .setTaskId(musicData.id()).setModel(musicData.modelName())
103   - .setPrompt(musicData.prompt()).setGptDescriptionPrompt(musicData.gptDescriptionPrompt())
104   - .setAudioUrl(musicData.audioUrl()).setVideoUrl(musicData.videoUrl()).setImageUrl(musicData.imageUrl())
105   - .setTitle(musicData.title()).setLyric(musicData.lyric()).setTags(StrUtil.split(musicData.tags(), StrPool.COMMA))
106   - .setStatus(Objects.equals("complete", musicData.status()) ?
107   - AiMusicStatusEnum.SUCCESS.getStatus() : AiMusicStatusEnum.IN_PROGRESS.getStatus()));
108   - }
109   -
110 98 @Override
111 99 public void updateMusicPublicStatus(AiMusicUpdatePublicStatusReqVO updateReqVO) {
112 100 // 校验存在
... ... @@ -116,6 +104,14 @@ public class AiMusicServiceImpl implements AiMusicService {
116 104 }
117 105  
118 106 @Override
  107 + public void updateMusicTitle(AiMusicUpdateTitleReqVO updateReqVO) {
  108 + // 校验存在
  109 + validateMusicExists(updateReqVO.getId());
  110 + // 更新
  111 + musicMapper.updateById(new AiMusicDO().setId(updateReqVO.getId()).setTitle(updateReqVO.getTitle()));
  112 + }
  113 +
  114 + @Override
119 115 public void deleteMusic(Long id) {
120 116 // 校验存在
121 117 validateMusicExists(id);
... ... @@ -123,10 +119,20 @@ public class AiMusicServiceImpl implements AiMusicService {
123 119 musicMapper.deleteById(id);
124 120 }
125 121  
126   - private void validateMusicExists(Long id) {
127   - if (musicMapper.selectById(id) == null) {
128   - throw exception(MUSIC_NOT_EXISTS);
  122 + @Override
  123 + public void deleteMusicMy(Long id, Long userId) {
  124 + // 1. 校验是否存在
  125 + AiMusicDO music = validateMusicExists(id);
  126 + if (ObjUtil.notEqual(music.getUserId(), userId)) {
  127 + throw exception(IMAGE_NOT_EXISTS);
129 128 }
  129 + // 2. 删除记录
  130 + musicMapper.deleteById(id);
  131 + }
  132 +
  133 + @Override
  134 + public AiMusicDO getMusic(Long id) {
  135 + return musicMapper.selectById(id);
130 136 }
131 137  
132 138 @Override
... ... @@ -134,4 +140,53 @@ public class AiMusicServiceImpl implements AiMusicService {
134 140 return musicMapper.selectPage(pageReqVO);
135 141 }
136 142  
  143 + @Override
  144 + public PageResult<AiMusicDO> getMusicMyPage(AiMusicPageReqVO pageReqVO, Long userId) {
  145 + return musicMapper.selectPageByMy(pageReqVO, userId);
  146 + }
  147 +
  148 + /**
  149 + * 构建 AiMusicDO 集合
  150 + *
  151 + * @param musicList suno 音乐任务列表
  152 + * @return AiMusicDO 集合
  153 + */
  154 + private List<AiMusicDO> buildMusicDOList(List<SunoApi.MusicData> musicList) {
  155 + return convertList(musicList, musicData -> new AiMusicDO()
  156 + .setTaskId(musicData.id()).setModel(musicData.modelName())
  157 + .setPrompt(musicData.prompt()).setGptDescriptionPrompt(musicData.gptDescriptionPrompt())
  158 + .setAudioUrl(createFile(musicData.audioUrl())).setVideoUrl(createFile(musicData.videoUrl())).setImageUrl(createFile(musicData.imageUrl())).setDuration(musicData.duration())
  159 + .setTitle(musicData.title()).setLyric(musicData.lyric()).setTags(StrUtil.split(musicData.tags(), StrPool.COMMA))
  160 + .setStatus(Objects.equals("complete", musicData.status()) ?
  161 + AiMusicStatusEnum.SUCCESS.getStatus() : AiMusicStatusEnum.IN_PROGRESS.getStatus()));
  162 + }
  163 +
  164 + /**
  165 + * 将生成的音频文件上传到文件服务器
  166 + *
  167 + * @param url 音频文件地址
  168 + * @return 内部文件地址
  169 + */
  170 + private String createFile(String url) {
  171 + if (StrUtil.isBlank(url)) {
  172 + return null;
  173 + }
  174 + byte[] bytes = HttpUtil.downloadBytes(url);
  175 + return fileApi.createFile(bytes);
  176 + }
  177 +
  178 + /**
  179 + * 校验音乐是否存在
  180 + *
  181 + * @param id 音乐编号
  182 + * @return 音乐信息
  183 + */
  184 + private AiMusicDO validateMusicExists(Long id) {
  185 + AiMusicDO music = musicMapper.selectById(id);
  186 + if (music == null) {
  187 + throw exception(MUSIC_NOT_EXISTS);
  188 + }
  189 + return music;
  190 + }
  191 +
137 192 }
... ...
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 {
117 117 * @param prompt 用于生成音乐音频的提示
118 118 * @param tags 音乐风格
119 119 * @param title 音乐名称
120   - * @param mv 模型
  120 + * @param model 模型
121 121 * @param waitAudio false 表示后台模式,仅返回音频任务信息,需要调用 get API 获取详细的音频信息。
122 122 * true 表示同步模式,API 最多等待 100s,音频生成完毕后直接返回音频链接等信息,建议在 GPT 等 agent 中使用。
123 123 * @param makeInstrumental 指示音乐音频是否为定制,如果为 true,则从歌词生成,否则从提示生成
... ... @@ -127,7 +127,7 @@ public class SunoApi {
127 127 String prompt,
128 128 String tags,
129 129 String title,
130   - String mv,
  130 + String model,
131 131 @JsonProperty("wait_audio") boolean waitAudio,
132 132 @JsonProperty("make_instrumental") boolean makeInstrumental
133 133 ) {
... ... @@ -136,12 +136,12 @@ public class SunoApi {
136 136 this(prompt, null, null, null, false, false);
137 137 }
138 138  
139   - public MusicGenerateRequest(String prompt, String mv, boolean makeInstrumental) {
140   - this(prompt, null, null, mv, false, makeInstrumental);
  139 + public MusicGenerateRequest(String prompt, String model, boolean makeInstrumental) {
  140 + this(prompt, null, null, model, false, makeInstrumental);
141 141 }
142 142  
143   - public MusicGenerateRequest(String prompt, String mv, String tags, String title) {
144   - this(prompt, tags, title, mv, false, false);
  143 + public MusicGenerateRequest(String prompt, String model, String tags, String title) {
  144 + this(prompt, tags, title, model, false, false);
145 145 }
146 146  
147 147 }
... ... @@ -162,6 +162,7 @@ public class SunoApi {
162 162 * @param prompt 生成音乐音频的提示
163 163 * @param type 操作类型
164 164 * @param tags 音乐类型标签
  165 + * @param duration 音乐时长
165 166 */
166 167 public record MusicData(
167 168 String id,
... ... @@ -176,7 +177,8 @@ public class SunoApi {
176 177 @JsonProperty("gpt_description_prompt") String gptDescriptionPrompt,
177 178 String prompt,
178 179 String type,
179   - String tags
  180 + String tags,
  181 + Double duration
180 182 ) {
181 183 }
182 184  
... ...
yudao-server/src/main/resources/application.yaml
... ... @@ -201,8 +201,8 @@ yudao.ai:
201 201 notify-url: http://java.nat300.top/admin-api/ai/image/midjourney/notify
202 202 suno:
203 203 enable: true
204   -# base-url: https://suno-om0w1cy6e-status2xxs-projects.vercel.app
205   - base-url: http://127.0.0.1:3001
  204 + base-url: https://suno-55ishh05u-status2xxs-projects.vercel.app
  205 +# base-url: http://127.0.0.1:3001
206 206  
207 207 --- #################### 芋道相关配置 ####################
208 208  
... ...