Commit f1292fb7fe3cfc41430ddd5ddd8f2af0a993ae0d
1 parent
085ebfc7
【增加】AI Music支持描述模式、歌词模式生成音乐
Showing
18 changed files
with
291 additions
and
311 deletions
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiModelEnum.java
| @@ -49,6 +49,11 @@ public enum AiModelEnum { | @@ -49,6 +49,11 @@ public enum AiModelEnum { | ||
| 49 | XING_HUO_2_0("星火大模型2.0", "generalv2", "/v2.1/chat"), | 49 | XING_HUO_2_0("星火大模型2.0", "generalv2", "/v2.1/chat"), |
| 50 | XING_HUO_3_0("星火大模型3.0", "generalv3", "/v3.1/chat"), | 50 | XING_HUO_3_0("星火大模型3.0", "generalv3", "/v3.1/chat"), |
| 51 | XING_HUO_3_5("星火大模型3.5", "generalv3.5", "/v3.5/chat"), | 51 | XING_HUO_3_5("星火大模型3.5", "generalv3.5", "/v3.5/chat"), |
| 52 | + | ||
| 53 | + //Suno | ||
| 54 | + SUNO_2( "SUNO-2", "chirp-v2-xxl-alpha",null), | ||
| 55 | + SUNO_3_0( "SUNO-3.0", "chirp-v3-0",null), | ||
| 56 | + SUNO_3_5( "SUNO-3.5", "chirp-v3.5",null), | ||
| 52 | ; | 57 | ; |
| 53 | 58 | ||
| 54 | /** | 59 | /** |
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiMusicStatusEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.enums; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Getter; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * @Author xiaoxin | ||
| 8 | + * @Date 2024/6/5 | ||
| 9 | + */ | ||
| 10 | +@AllArgsConstructor | ||
| 11 | +@Getter | ||
| 12 | +public enum AiMusicStatusEnum { | ||
| 13 | + | ||
| 14 | + SUBMITTED("submitted", "已提交"), | ||
| 15 | + QUEUED("queued", "排队中"), | ||
| 16 | + STREAMING("streaming", "进行中"), | ||
| 17 | + COMPLETE("complete", "完成"); | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 状态 | ||
| 21 | + */ | ||
| 22 | + private final String status; | ||
| 23 | + /** | ||
| 24 | + * 状态名 | ||
| 25 | + */ | ||
| 26 | + private final String name; | ||
| 27 | + | ||
| 28 | + public static AiMusicStatusEnum valueOfStatus(String status) { | ||
| 29 | + for (AiMusicStatusEnum statusEnum : AiMusicStatusEnum.values()) { | ||
| 30 | + if (statusEnum.getStatus().equals(status)) { | ||
| 31 | + return statusEnum; | ||
| 32 | + } | ||
| 33 | + } | ||
| 34 | + throw new IllegalArgumentException("未知会话状态: " + status); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | +} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/MusicController.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.music; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.music; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoLyricModeVO; | ||
| 4 | import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoReqVO; | 5 | import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoReqVO; |
| 5 | -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoRespVO; | ||
| 6 | import cn.iocoder.yudao.module.ai.service.music.MusicService; | 6 | import cn.iocoder.yudao.module.ai.service.music.MusicService; |
| 7 | import io.swagger.v3.oas.annotations.Operation; | 7 | import io.swagger.v3.oas.annotations.Operation; |
| 8 | import io.swagger.v3.oas.annotations.tags.Tag; | 8 | import io.swagger.v3.oas.annotations.tags.Tag; |
| @@ -13,6 +13,8 @@ import org.springframework.web.bind.annotation.RequestBody; | @@ -13,6 +13,8 @@ import org.springframework.web.bind.annotation.RequestBody; | ||
| 13 | import org.springframework.web.bind.annotation.RequestMapping; | 13 | import org.springframework.web.bind.annotation.RequestMapping; |
| 14 | import org.springframework.web.bind.annotation.RestController; | 14 | import org.springframework.web.bind.annotation.RestController; |
| 15 | 15 | ||
| 16 | +import java.util.List; | ||
| 17 | + | ||
| 16 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 18 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 17 | 19 | ||
| 18 | @Tag(name = "管理后台 - AI 音乐生成") | 20 | @Tag(name = "管理后台 - AI 音乐生成") |
| @@ -23,9 +25,16 @@ public class MusicController { | @@ -23,9 +25,16 @@ public class MusicController { | ||
| 23 | 25 | ||
| 24 | private final MusicService musicService; | 26 | private final MusicService musicService; |
| 25 | 27 | ||
| 26 | - @PostMapping("/suno-gen") | ||
| 27 | - @Operation(summary = "音乐生成") | ||
| 28 | - public CommonResult<SunoRespVO> musicGen(@RequestBody @Valid SunoReqVO sunoReqVO) { | ||
| 29 | - return success(musicService.musicGen(sunoReqVO)); | 28 | + @PostMapping("generate/description-mode") |
| 29 | + @Operation(summary = "音乐生成-描述模式") | ||
| 30 | + public CommonResult<List<Long>> descriptionMode(@RequestBody @Valid SunoReqVO sunoReqVO) { | ||
| 31 | + return success(musicService.descriptionMode(sunoReqVO)); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + @PostMapping("generate/lyric-mode") | ||
| 35 | + @Operation(summary = "音乐生成-歌词模式") | ||
| 36 | + public CommonResult<List<Long>> lyricMode(@RequestBody @Valid SunoLyricModeVO sunoLyricModeVO) { | ||
| 37 | + return success(musicService.lyricMode(sunoLyricModeVO)); | ||
| 30 | } | 38 | } |
| 39 | + | ||
| 31 | } | 40 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/MusicDataVO.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.music.vo; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.ai.core.model.suno.api.AceDataSunoApi; | ||
| 4 | -import com.fasterxml.jackson.annotation.JsonProperty; | ||
| 5 | -import lombok.Data; | ||
| 6 | - | ||
| 7 | -import java.util.List; | ||
| 8 | -import java.util.stream.Collectors; | ||
| 9 | - | ||
| 10 | -/** | ||
| 11 | - * 表示单个音乐数据的类 | ||
| 12 | - */ | ||
| 13 | -@Data | ||
| 14 | -public class MusicDataVO { | ||
| 15 | - /** | ||
| 16 | - * 音乐数据的 ID | ||
| 17 | - */ | ||
| 18 | - private String id; | ||
| 19 | - | ||
| 20 | - /** | ||
| 21 | - * 音乐音频的标题 | ||
| 22 | - */ | ||
| 23 | - private String title; | ||
| 24 | - | ||
| 25 | - /** | ||
| 26 | - * 音乐音频的图片 URL | ||
| 27 | - */ | ||
| 28 | - @JsonProperty("image_url") | ||
| 29 | - private String imageUrl; | ||
| 30 | - | ||
| 31 | - /** | ||
| 32 | - * 音乐音频的歌词 | ||
| 33 | - */ | ||
| 34 | - private String lyric; | ||
| 35 | - | ||
| 36 | - /** | ||
| 37 | - * 音乐音频的 URL | ||
| 38 | - */ | ||
| 39 | - @JsonProperty("audio_url") | ||
| 40 | - private String audioUrl; | ||
| 41 | - | ||
| 42 | - /** | ||
| 43 | - * 音乐视频的 URL | ||
| 44 | - */ | ||
| 45 | - @JsonProperty("video_url") | ||
| 46 | - private String videoUrl; | ||
| 47 | - | ||
| 48 | - /** | ||
| 49 | - * 音乐音频的创建时间 | ||
| 50 | - */ | ||
| 51 | - @JsonProperty("created_at") | ||
| 52 | - private String createdAt; | ||
| 53 | - | ||
| 54 | - /** | ||
| 55 | - * 使用的模型名称 | ||
| 56 | - */ | ||
| 57 | - private String model; | ||
| 58 | - | ||
| 59 | - /** | ||
| 60 | - * 生成音乐音频的提示 | ||
| 61 | - */ | ||
| 62 | - private String prompt; | ||
| 63 | - | ||
| 64 | - /** | ||
| 65 | - * 音乐音频的风格 | ||
| 66 | - */ | ||
| 67 | - private String style; | ||
| 68 | - | ||
| 69 | - public static List<MusicDataVO> convertFrom(List<AceDataSunoApi.SunoResp.MusicData> musicDataList) { | ||
| 70 | - return musicDataList.stream().map(musicData -> { | ||
| 71 | - MusicDataVO musicDataVO = new MusicDataVO(); | ||
| 72 | - musicDataVO.setId(musicData.id()); | ||
| 73 | - musicDataVO.setTitle(musicData.title()); | ||
| 74 | - musicDataVO.setImageUrl(musicData.imageUrl()); | ||
| 75 | - musicDataVO.setLyric(musicData.lyric()); | ||
| 76 | - musicDataVO.setAudioUrl(musicData.audioUrl()); | ||
| 77 | - musicDataVO.setVideoUrl(musicData.videoUrl()); | ||
| 78 | - musicDataVO.setCreatedAt(musicData.createdAt()); | ||
| 79 | - musicDataVO.setModel(musicData.model()); | ||
| 80 | - musicDataVO.setPrompt(musicData.prompt()); | ||
| 81 | - musicDataVO.setStyle(musicData.style()); | ||
| 82 | - return musicDataVO; | ||
| 83 | - }).collect(Collectors.toList()); | ||
| 84 | - } | ||
| 85 | -} | ||
| 86 | \ No newline at end of file | 0 | \ 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/SunoLyricModeVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.music.vo; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * @Author jxli@quant360.com | ||
| 7 | + * @Date 2024/6/7 | ||
| 8 | + */ | ||
| 9 | +@Data | ||
| 10 | +public class SunoLyricModeVO extends SunoReqVO { | ||
| 11 | + | ||
| 12 | + /** | ||
| 13 | + * 标签/音乐风格 | ||
| 14 | + */ | ||
| 15 | + private String tags; | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 音乐名称 | ||
| 19 | + */ | ||
| 20 | + private String title; | ||
| 21 | + | ||
| 22 | +} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/SunoReqVO.java
| @@ -2,39 +2,21 @@ package cn.iocoder.yudao.module.ai.controller.admin.music.vo; | @@ -2,39 +2,21 @@ package cn.iocoder.yudao.module.ai.controller.admin.music.vo; | ||
| 2 | 2 | ||
| 3 | import com.fasterxml.jackson.annotation.JsonInclude; | 3 | import com.fasterxml.jackson.annotation.JsonInclude; |
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | -import lombok.experimental.Accessors; | ||
| 6 | 5 | ||
| 7 | @Data | 6 | @Data |
| 8 | -@Accessors(chain = true) | ||
| 9 | @JsonInclude(value = JsonInclude.Include.NON_NULL) | 7 | @JsonInclude(value = JsonInclude.Include.NON_NULL) |
| 10 | public class SunoReqVO { | 8 | public class SunoReqVO { |
| 11 | /** | 9 | /** |
| 12 | * 用于生成音乐音频的提示 | 10 | * 用于生成音乐音频的提示 |
| 13 | */ | 11 | */ |
| 14 | private String prompt; | 12 | private String prompt; |
| 15 | - | ||
| 16 | - /** | ||
| 17 | - * 用于生成音乐音频的歌词 | ||
| 18 | - */ | ||
| 19 | - private String lyric; | ||
| 20 | - | ||
| 21 | /** | 13 | /** |
| 22 | - * 指示音乐音频是否为定制,如果为 true,则从歌词生成,否则从提示生成 | 14 | + * 是否纯音乐 |
| 23 | */ | 15 | */ |
| 24 | - private boolean custom; | ||
| 25 | - | ||
| 26 | - /** | ||
| 27 | - * 音乐音频的标题 | ||
| 28 | - */ | ||
| 29 | - private String title; | ||
| 30 | - | ||
| 31 | - /** | ||
| 32 | - * 音乐音频的风格 | ||
| 33 | - */ | ||
| 34 | - private String style; | ||
| 35 | - | 16 | + private boolean makeInstrumental; |
| 36 | /** | 17 | /** |
| 37 | - * 音乐音频生成后回调的 URL | 18 | + * //todo 首次请求返回的模型是对的,后续更新音频返回的模型又变成v3.5了 |
| 19 | + * 模型版本 {@link cn.iocoder.yudao.module.ai.enums.AiModelEnum} Suno | ||
| 38 | */ | 20 | */ |
| 39 | - private String callbackUrl; | 21 | + private String mv; |
| 40 | } | 22 | } |
| 41 | \ No newline at end of file | 23 | \ 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/SunoRespVO.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.music.vo; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.ai.core.model.suno.api.AceDataSunoApi; | ||
| 4 | -import com.fasterxml.jackson.annotation.JsonProperty; | ||
| 5 | -import lombok.Data; | ||
| 6 | - | ||
| 7 | -import java.util.List; | ||
| 8 | - | ||
| 9 | -/** | ||
| 10 | - * API 响应的数据 | ||
| 11 | - */ | ||
| 12 | -@Data | ||
| 13 | -public class SunoRespVO { | ||
| 14 | - /** | ||
| 15 | - * 表示请求是否成功 | ||
| 16 | - */ | ||
| 17 | - private boolean success; | ||
| 18 | - | ||
| 19 | - /** | ||
| 20 | - * 任务 ID | ||
| 21 | - */ | ||
| 22 | - @JsonProperty("task_id") | ||
| 23 | - private String taskId; | ||
| 24 | - | ||
| 25 | - /** | ||
| 26 | - * 音乐数据列表 | ||
| 27 | - */ | ||
| 28 | - private List<MusicDataVO> data; | ||
| 29 | - | ||
| 30 | - | ||
| 31 | - //把 SunoResp转为本vo类 | ||
| 32 | - public static SunoRespVO convertFrom(AceDataSunoApi.SunoResp sunoResp) { | ||
| 33 | - SunoRespVO sunoRespVO = new SunoRespVO(); | ||
| 34 | - sunoRespVO.setSuccess(sunoResp.success()); | ||
| 35 | - sunoRespVO.setTaskId(sunoResp.taskId()); | ||
| 36 | - sunoRespVO.setData(MusicDataVO.convertFrom(sunoResp.data())); | ||
| 37 | - return sunoRespVO; | ||
| 38 | - } | ||
| 39 | - | ||
| 40 | -} | ||
| 41 | \ No newline at end of file | 0 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.dal.dataobject.music; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; | ||
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | ||
| 5 | +import com.baomidou.mybatisplus.annotation.IdType; | ||
| 6 | +import com.baomidou.mybatisplus.annotation.TableId; | ||
| 7 | +import com.baomidou.mybatisplus.annotation.TableName; | ||
| 8 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 9 | +import lombok.Data; | ||
| 10 | + | ||
| 11 | +import java.util.List; | ||
| 12 | +import java.util.stream.Collectors; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * @Author xiaoxin | ||
| 16 | + * @Date 2024/6/5 | ||
| 17 | + */ | ||
| 18 | +@TableName("ai_music") | ||
| 19 | +@Data | ||
| 20 | +public class AiMusicDO extends BaseDO { | ||
| 21 | + @TableId(type = IdType.AUTO) | ||
| 22 | + @Schema(description = "编号") | ||
| 23 | + private Long id; | ||
| 24 | + | ||
| 25 | + @Schema(description = "用户编号") | ||
| 26 | + private Long userId; | ||
| 27 | + | ||
| 28 | + @Schema(description = "音乐名称") | ||
| 29 | + private String title; | ||
| 30 | + | ||
| 31 | + @Schema(description = "图片地址") | ||
| 32 | + private String imageUrl; | ||
| 33 | + | ||
| 34 | + @Schema(description = "歌词") | ||
| 35 | + private String lyric; | ||
| 36 | + | ||
| 37 | + @Schema(description = "音频地址") | ||
| 38 | + private String audioUrl; | ||
| 39 | + | ||
| 40 | + @Schema(description = "视频地址") | ||
| 41 | + private String videoUrl; | ||
| 42 | + | ||
| 43 | + @Schema(description = "音乐状态") | ||
| 44 | + private String status; | ||
| 45 | + | ||
| 46 | + @Schema(description = "描述词") | ||
| 47 | + private String gptDescriptionPrompt; | ||
| 48 | + | ||
| 49 | + @Schema(description = "提示词") | ||
| 50 | + private String prompt; | ||
| 51 | + | ||
| 52 | + @Schema(description = "模型") | ||
| 53 | + private String model; | ||
| 54 | + | ||
| 55 | + @Schema(description = "错误信息") | ||
| 56 | + private String errorMessage; | ||
| 57 | + | ||
| 58 | + @Schema(description = "音乐风格标签") | ||
| 59 | + private String tags; | ||
| 60 | + | ||
| 61 | + @Schema(description = "任务id") | ||
| 62 | + private String taskId; | ||
| 63 | + | ||
| 64 | + | ||
| 65 | + | ||
| 66 | + public static AiMusicDO convertFrom(SunoApi.MusicData musicData) { | ||
| 67 | + return new AiMusicDO() | ||
| 68 | + .setTaskId(musicData.id()) | ||
| 69 | + .setPrompt(musicData.prompt()) | ||
| 70 | + .setGptDescriptionPrompt(musicData.gptDescriptionPrompt()) | ||
| 71 | + .setAudioUrl(musicData.audioUrl()) | ||
| 72 | + .setVideoUrl(musicData.videoUrl()) | ||
| 73 | + .setImageUrl(musicData.imageUrl()) | ||
| 74 | + .setLyric(musicData.lyric()) | ||
| 75 | + .setTitle(musicData.title()) | ||
| 76 | + .setStatus(musicData.status()) | ||
| 77 | + .setModel(musicData.modelName()) | ||
| 78 | + .setTags(musicData.tags()); | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + public static List<AiMusicDO> convertFrom(List<SunoApi.MusicData> musicDataList) { | ||
| 82 | + return musicDataList.stream() | ||
| 83 | + .map(AiMusicDO::convertFrom) | ||
| 84 | + .collect(Collectors.toList()); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + | ||
| 88 | +} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/music/AiMusicMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.dal.mysql.music; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 4 | +import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO; | ||
| 5 | +import org.apache.ibatis.annotations.Mapper; | ||
| 6 | + | ||
| 7 | +/** | ||
| 8 | + * @Author xiaoxin | ||
| 9 | + * @Date 2024/6/5 | ||
| 10 | + */ | ||
| 11 | +@Mapper | ||
| 12 | +public interface AiMusicMapper extends BaseMapperX<AiMusicDO> { | ||
| 13 | + | ||
| 14 | +} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/MusicService.java
| 1 | package cn.iocoder.yudao.module.ai.service.music; | 1 | package cn.iocoder.yudao.module.ai.service.music; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoLyricModeVO; | ||
| 3 | import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoReqVO; | 4 | import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoReqVO; |
| 4 | -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoRespVO; | 5 | + |
| 6 | +import java.util.List; | ||
| 5 | 7 | ||
| 6 | /** | 8 | /** |
| 7 | * @Author xiaoxin | 9 | * @Author xiaoxin |
| @@ -10,10 +12,13 @@ import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoRespVO; | @@ -10,10 +12,13 @@ import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoRespVO; | ||
| 10 | public interface MusicService { | 12 | public interface MusicService { |
| 11 | 13 | ||
| 12 | /** | 14 | /** |
| 13 | - * 音乐生成 | ||
| 14 | - * | ||
| 15 | - * @param sunoReqVO 请求实体 | ||
| 16 | - * @return 响应实体 | 15 | + * 音乐生成-描述模式 |
| 17 | */ | 16 | */ |
| 18 | - SunoRespVO musicGen(SunoReqVO sunoReqVO); | 17 | + List<Long> descriptionMode(SunoReqVO reqVO); |
| 18 | + | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * 音乐生成-歌词模式 | ||
| 22 | + **/ | ||
| 23 | + List<Long> lyricMode(SunoLyricModeVO reqVO); | ||
| 19 | } | 24 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/MusicServiceImpl.java
| 1 | package cn.iocoder.yudao.module.ai.service.music; | 1 | package cn.iocoder.yudao.module.ai.service.music; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.ai.core.model.suno.api.AceDataSunoApi; | 3 | +import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.text.StrPool; | ||
| 5 | +import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; | ||
| 6 | +import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoLyricModeVO; | ||
| 4 | import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoReqVO; | 7 | import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoReqVO; |
| 5 | -import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoRespVO; | 8 | +import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO; |
| 9 | +import cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper; | ||
| 10 | +import cn.iocoder.yudao.module.ai.enums.AiMusicStatusEnum; | ||
| 11 | +import com.baomidou.mybatisplus.core.toolkit.Wrappers; | ||
| 6 | import lombok.RequiredArgsConstructor; | 12 | import lombok.RequiredArgsConstructor; |
| 13 | +import lombok.extern.slf4j.Slf4j; | ||
| 14 | +import org.springframework.scheduling.annotation.Scheduled; | ||
| 7 | import org.springframework.stereotype.Service; | 15 | import org.springframework.stereotype.Service; |
| 16 | +import org.springframework.transaction.annotation.Transactional; | ||
| 17 | + | ||
| 18 | +import java.util.*; | ||
| 19 | +import java.util.concurrent.ConcurrentLinkedQueue; | ||
| 20 | +import java.util.concurrent.TimeUnit; | ||
| 21 | +import java.util.stream.Collectors; | ||
| 22 | + | ||
| 23 | +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; | ||
| 8 | 24 | ||
| 9 | /** | 25 | /** |
| 10 | * @Author xiaoxin | 26 | * @Author xiaoxin |
| @@ -12,20 +28,70 @@ import org.springframework.stereotype.Service; | @@ -12,20 +28,70 @@ import org.springframework.stereotype.Service; | ||
| 12 | */ | 28 | */ |
| 13 | @Service | 29 | @Service |
| 14 | @RequiredArgsConstructor | 30 | @RequiredArgsConstructor |
| 31 | +@Slf4j | ||
| 15 | public class MusicServiceImpl implements MusicService { | 32 | public class MusicServiceImpl implements MusicService { |
| 16 | 33 | ||
| 17 | - private final AceDataSunoApi aceDataSunoApi; | 34 | + private final SunoApi sunoApi; |
| 35 | + private final AiMusicMapper musicMapper; | ||
| 36 | + | ||
| 37 | + private final Queue<String> taskQueue = new ConcurrentLinkedQueue<>(); | ||
| 38 | + | ||
| 18 | 39 | ||
| 19 | @Override | 40 | @Override |
| 20 | - public SunoRespVO musicGen(SunoReqVO sunoReqVO) { | ||
| 21 | - AceDataSunoApi.SunoResp sunoResp = aceDataSunoApi.musicGen(new AceDataSunoApi.SunoReq( | ||
| 22 | - sunoReqVO.getPrompt(), | ||
| 23 | - sunoReqVO.getLyric(), | ||
| 24 | - sunoReqVO.isCustom(), | ||
| 25 | - sunoReqVO.getTitle(), | ||
| 26 | - sunoReqVO.getStyle(), | ||
| 27 | - sunoReqVO.getCallbackUrl() | ||
| 28 | - )); | ||
| 29 | - return SunoRespVO.convertFrom(sunoResp); | 41 | + public List<Long> descriptionMode(SunoReqVO reqVO) { |
| 42 | + SunoApi.SunoReq sunoReq = new SunoApi.SunoReq(reqVO.getPrompt(), reqVO.getMv(), reqVO.isMakeInstrumental()); | ||
| 43 | + //默认异步 | ||
| 44 | + List<SunoApi.MusicData> musicDataList = sunoApi.generate(sunoReq); | ||
| 45 | + return insertMusicData(musicDataList); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + | ||
| 49 | + @Override | ||
| 50 | + public List<Long> lyricMode(SunoLyricModeVO reqVO) { | ||
| 51 | + SunoApi.SunoReq sunoReq = new SunoApi.SunoReq(reqVO.getPrompt(), reqVO.getMv(), reqVO.getTags(), reqVO.getTitle()); | ||
| 52 | + //默认异步 | ||
| 53 | + List<SunoApi.MusicData> musicDataList = sunoApi.customGenerate(sunoReq); | ||
| 54 | + return insertMusicData(musicDataList); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + /** | ||
| 58 | + * 新增音乐数据并提交 suno任务 | ||
| 59 | + * | ||
| 60 | + * @param musicDataList 音乐数据列表 | ||
| 61 | + * @return 音乐id集合 | ||
| 62 | + */ | ||
| 63 | + private List<Long> insertMusicData(List<SunoApi.MusicData> musicDataList) { | ||
| 64 | + if (CollUtil.isEmpty(musicDataList)) { | ||
| 65 | + return Collections.emptyList(); | ||
| 66 | + } | ||
| 67 | + return AiMusicDO.convertFrom(musicDataList).stream() | ||
| 68 | + .peek(musicDO -> musicMapper.insert(musicDO.setUserId(getLoginUserId()))) | ||
| 69 | + .peek(e -> Optional.of(e.getTaskId()).ifPresent(taskQueue::add)) | ||
| 70 | + .map(AiMusicDO::getId) | ||
| 71 | + .collect(Collectors.toList()); | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + @Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS) | ||
| 75 | + @Transactional | ||
| 76 | + public void flushSunoTask() { | ||
| 77 | + if (CollUtil.isEmpty(taskQueue)) { | ||
| 78 | + return; | ||
| 79 | + } | ||
| 80 | + CollUtil.split(taskQueue, 5). | ||
| 81 | + stream().map(chunk -> CollUtil.join(chunk, StrPool.COMMA)) | ||
| 82 | + .forEach(taskIds -> { | ||
| 83 | + List<SunoApi.MusicData> musicData = sunoApi.selectById(taskIds); | ||
| 84 | + musicData.stream() | ||
| 85 | + .map(AiMusicDO::convertFrom) | ||
| 86 | + .forEach(musicDO -> { | ||
| 87 | + //更新音乐生成结果 | ||
| 88 | + musicMapper.update(musicDO, Wrappers.<AiMusicDO>lambdaUpdate().eq(AiMusicDO::getTaskId, musicDO.getTaskId())); | ||
| 89 | + //完成后剔除任务 | ||
| 90 | + if (Objects.equals(AiMusicStatusEnum.COMPLETE.getStatus(), musicDO.getStatus())) { | ||
| 91 | + taskQueue.remove(musicDO.getTaskId()); | ||
| 92 | + } | ||
| 93 | + }); | ||
| 94 | + }); | ||
| 95 | + | ||
| 30 | } | 96 | } |
| 31 | } | 97 | } |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
| @@ -4,7 +4,7 @@ import cn.hutool.core.io.IoUtil; | @@ -4,7 +4,7 @@ import cn.hutool.core.io.IoUtil; | ||
| 4 | import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactory; | 4 | import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactory; |
| 5 | import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactoryImpl; | 5 | import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactoryImpl; |
| 6 | import cn.iocoder.yudao.framework.ai.core.model.suno.SunoConfig; | 6 | import cn.iocoder.yudao.framework.ai.core.model.suno.SunoConfig; |
| 7 | -import cn.iocoder.yudao.framework.ai.core.model.suno.api.AceDataSunoApi; | 7 | +import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; |
| 8 | import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenChatClient; | 8 | import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenChatClient; |
| 9 | import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenChatModal; | 9 | import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenChatModal; |
| 10 | import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenOptions; | 10 | import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenOptions; |
| @@ -150,8 +150,8 @@ public class YudaoAiAutoConfiguration { | @@ -150,8 +150,8 @@ public class YudaoAiAutoConfiguration { | ||
| 150 | 150 | ||
| 151 | @Bean | 151 | @Bean |
| 152 | @ConditionalOnProperty(value = "yudao.ai.suno.enable", havingValue = "true") | 152 | @ConditionalOnProperty(value = "yudao.ai.suno.enable", havingValue = "true") |
| 153 | - public AceDataSunoApi sunoApi(YudaoAiProperties yudaoAiProperties) { | ||
| 154 | - return new AceDataSunoApi(new SunoConfig(yudaoAiProperties.getSuno().getToken())); | 153 | + public SunoApi sunoApi(YudaoAiProperties yudaoAiProperties) { |
| 154 | + return new SunoApi(new SunoConfig(yudaoAiProperties.getSuno().getBaseUrl())); | ||
| 155 | } | 155 | } |
| 156 | 156 | ||
| 157 | private static @NotNull MidjourneyConfig getMidjourneyConfig(ApplicationContext applicationContext, | 157 | private static @NotNull MidjourneyConfig getMidjourneyConfig(ApplicationContext applicationContext, |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java
| @@ -141,9 +141,9 @@ public class YudaoAiProperties { | @@ -141,9 +141,9 @@ public class YudaoAiProperties { | ||
| 141 | 141 | ||
| 142 | private boolean enable = false; | 142 | private boolean enable = false; |
| 143 | /** | 143 | /** |
| 144 | - * token | 144 | + * suno-api 服务的基本地址 |
| 145 | */ | 145 | */ |
| 146 | - private String token; | 146 | + private String baseUrl; |
| 147 | 147 | ||
| 148 | } | 148 | } |
| 149 | 149 |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/suno/SunoConfig.java
| @@ -13,7 +13,7 @@ import lombok.NoArgsConstructor; | @@ -13,7 +13,7 @@ import lombok.NoArgsConstructor; | ||
| 13 | @AllArgsConstructor | 13 | @AllArgsConstructor |
| 14 | public class SunoConfig { | 14 | public class SunoConfig { |
| 15 | /** | 15 | /** |
| 16 | - * token信息 | 16 | + * suno-api服务的基本路径 |
| 17 | */ | 17 | */ |
| 18 | - private String token; | 18 | + private String baseUrl; |
| 19 | } | 19 | } |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/suno/api/AceDataSunoApi.java deleted
| 1 | -package cn.iocoder.yudao.framework.ai.core.model.suno.api; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.ai.core.model.suno.SunoConfig; | ||
| 4 | -import com.fasterxml.jackson.annotation.JsonInclude; | ||
| 5 | -import com.fasterxml.jackson.annotation.JsonProperty; | ||
| 6 | -import lombok.extern.slf4j.Slf4j; | ||
| 7 | -import org.springframework.ai.openai.api.ApiUtils; | ||
| 8 | -import org.springframework.web.reactive.function.client.WebClient; | ||
| 9 | -import reactor.core.publisher.Mono; | ||
| 10 | - | ||
| 11 | -import java.util.List; | ||
| 12 | - | ||
| 13 | -/** | ||
| 14 | - * Suno API | ||
| 15 | - * <br> | ||
| 16 | - * 文档地址:https://platform.acedata.cloud/documents/d016ee3f-421b-4b6e-989a-8beba8701701 | ||
| 17 | - * | ||
| 18 | - * @Author xiaoxin | ||
| 19 | - * @Date 2024/5/27 | ||
| 20 | - */ | ||
| 21 | -@Slf4j | ||
| 22 | -public class AceDataSunoApi { | ||
| 23 | - | ||
| 24 | - public static final String DEFAULT_BASE_URL = "https://api.acedata.cloud/suno"; | ||
| 25 | - private final WebClient webClient; | ||
| 26 | - | ||
| 27 | - public AceDataSunoApi(SunoConfig config) { | ||
| 28 | - this.webClient = WebClient.builder() | ||
| 29 | - .baseUrl(DEFAULT_BASE_URL) | ||
| 30 | - .defaultHeaders(ApiUtils.getJsonContentHeaders(config.getToken())) | ||
| 31 | - .build(); | ||
| 32 | - } | ||
| 33 | - | ||
| 34 | - // TODO @芋艿:方法名,要考虑下; | ||
| 35 | - public SunoResp musicGen(SunoReq sunReq) { | ||
| 36 | - return this.webClient.post() | ||
| 37 | - .uri("/audios") | ||
| 38 | - .body(Mono.just(sunReq), SunoReq.class) | ||
| 39 | - .retrieve() | ||
| 40 | - .onStatus(status -> !status.is2xxSuccessful(), | ||
| 41 | - response -> response.bodyToMono(String.class) | ||
| 42 | - .handle((respBody, sink) -> { | ||
| 43 | - log.error("【Suno】调用失败!resp: 【{}】", respBody); | ||
| 44 | - sink.error(new IllegalStateException("【Suno】调用失败!")); | ||
| 45 | - })) | ||
| 46 | - .bodyToMono(SunoResp.class) | ||
| 47 | - .block(); | ||
| 48 | - } | ||
| 49 | - | ||
| 50 | - /** | ||
| 51 | - * 请求数据对象,用于生成音乐音频。 | ||
| 52 | - * | ||
| 53 | - * @param prompt 用于生成音乐音频的提示 | ||
| 54 | - * @param lyric 用于生成音乐音频的歌词 | ||
| 55 | - * @param custom 指示音乐音频是否为定制,如果为 true,则从歌词生成,否则从提示生成 | ||
| 56 | - * @param title 音乐音频的标题 | ||
| 57 | - * @param style 音乐音频的风格 | ||
| 58 | - * @param callbackUrl 音乐音频生成后回调的 URL | ||
| 59 | - */ | ||
| 60 | - @JsonInclude(value = JsonInclude.Include.NON_NULL) | ||
| 61 | - public record SunoReq( | ||
| 62 | - String prompt, | ||
| 63 | - String lyric, | ||
| 64 | - boolean custom, | ||
| 65 | - String title, | ||
| 66 | - String style, | ||
| 67 | - String callbackUrl | ||
| 68 | - ) { | ||
| 69 | - public SunoReq(String prompt) { | ||
| 70 | - this(prompt, null, false, null, null, null); | ||
| 71 | - } | ||
| 72 | - | ||
| 73 | - } | ||
| 74 | - | ||
| 75 | - /** | ||
| 76 | - * SunoAPI 响应的数据。 | ||
| 77 | - * | ||
| 78 | - * @param success 表示请求是否成功 | ||
| 79 | - * @param taskId 任务 ID | ||
| 80 | - * @param data 音乐数据列表 | ||
| 81 | - */ | ||
| 82 | - public record SunoResp( | ||
| 83 | - boolean success, | ||
| 84 | - @JsonProperty("task_id") String taskId, | ||
| 85 | - List<MusicData> data | ||
| 86 | - ) { | ||
| 87 | - /** | ||
| 88 | - * 单个音乐数据。 | ||
| 89 | - * | ||
| 90 | - * @param id 音乐数据的 ID | ||
| 91 | - * @param title 音乐音频的标题 | ||
| 92 | - * @param imageUrl 音乐音频的图片 URL | ||
| 93 | - * @param lyric 音乐音频的歌词 | ||
| 94 | - * @param audioUrl 音乐音频的 URL | ||
| 95 | - * @param videoUrl 音乐视频的 URL | ||
| 96 | - * @param createdAt 音乐音频的创建时间 | ||
| 97 | - * @param model 使用的模型名称 | ||
| 98 | - * @param prompt 生成音乐音频的提示 | ||
| 99 | - * @param style 音乐音频的风格 | ||
| 100 | - */ | ||
| 101 | - public record MusicData( | ||
| 102 | - String id, | ||
| 103 | - String title, | ||
| 104 | - @JsonProperty("image_url") String imageUrl, | ||
| 105 | - String lyric, | ||
| 106 | - @JsonProperty("audio_url") String audioUrl, | ||
| 107 | - @JsonProperty("video_url") String videoUrl, | ||
| 108 | - @JsonProperty("created_at") String createdAt, | ||
| 109 | - String model, | ||
| 110 | - String prompt, | ||
| 111 | - String style | ||
| 112 | - ) { | ||
| 113 | - } | ||
| 114 | - } | ||
| 115 | -} |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/suno/api/SunoApi.java
| 1 | package cn.iocoder.yudao.framework.ai.core.model.suno.api; | 1 | package cn.iocoder.yudao.framework.ai.core.model.suno.api; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.ai.core.model.suno.SunoConfig; | ||
| 3 | import com.fasterxml.jackson.annotation.JsonInclude; | 4 | import com.fasterxml.jackson.annotation.JsonInclude; |
| 4 | import com.fasterxml.jackson.annotation.JsonProperty; | 5 | import com.fasterxml.jackson.annotation.JsonProperty; |
| 5 | import lombok.extern.slf4j.Slf4j; | 6 | import lombok.extern.slf4j.Slf4j; |
| @@ -25,10 +26,7 @@ import java.util.function.Predicate; | @@ -25,10 +26,7 @@ import java.util.function.Predicate; | ||
| 25 | @Slf4j | 26 | @Slf4j |
| 26 | public class SunoApi { | 27 | public class SunoApi { |
| 27 | 28 | ||
| 28 | - public static final String DEFAULT_BASE_URL = "https://suno-9323szg26-status2xxs-projects.vercel.app"; | ||
| 29 | private final WebClient webClient; | 29 | private final WebClient webClient; |
| 30 | - | ||
| 31 | - | ||
| 32 | private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful(); | 30 | private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful(); |
| 33 | private final Function<ClientResponse, Mono<? extends Throwable>> EXCEPTION_FUNCTION = response -> response.bodyToMono(String.class) | 31 | private final Function<ClientResponse, Mono<? extends Throwable>> EXCEPTION_FUNCTION = response -> response.bodyToMono(String.class) |
| 34 | .handle((respBody, sink) -> { | 32 | .handle((respBody, sink) -> { |
| @@ -37,9 +35,9 @@ public class SunoApi { | @@ -37,9 +35,9 @@ public class SunoApi { | ||
| 37 | }); | 35 | }); |
| 38 | 36 | ||
| 39 | 37 | ||
| 40 | - public SunoApi() { | 38 | + public SunoApi(SunoConfig config) { |
| 41 | this.webClient = WebClient.builder() | 39 | this.webClient = WebClient.builder() |
| 42 | - .baseUrl(DEFAULT_BASE_URL) | 40 | + .baseUrl(config.getBaseUrl()) |
| 43 | .defaultHeaders((headers) -> headers.setContentType(MediaType.APPLICATION_JSON)) | 41 | .defaultHeaders((headers) -> headers.setContentType(MediaType.APPLICATION_JSON)) |
| 44 | .build(); | 42 | .build(); |
| 45 | } | 43 | } |
| @@ -141,8 +139,8 @@ public class SunoApi { | @@ -141,8 +139,8 @@ public class SunoApi { | ||
| 141 | } | 139 | } |
| 142 | 140 | ||
| 143 | 141 | ||
| 144 | - public SunoReq(String prompt, String tags, String title) { | ||
| 145 | - this(prompt, tags, title, null, false, false); | 142 | + public SunoReq(String prompt, String mv, String tags, String title) { |
| 143 | + this(prompt, tags, title, mv, false, false); | ||
| 146 | } | 144 | } |
| 147 | } | 145 | } |
| 148 | 146 |
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/suno/SunoTests.java
| @@ -13,24 +13,22 @@ import java.util.List; | @@ -13,24 +13,22 @@ import java.util.List; | ||
| 13 | */ | 13 | */ |
| 14 | public class SunoTests { | 14 | public class SunoTests { |
| 15 | 15 | ||
| 16 | - private SunoConfig sunoConfig; | 16 | + SunoApi sunoApi; |
| 17 | 17 | ||
| 18 | @Before | 18 | @Before |
| 19 | public void setup() { | 19 | public void setup() { |
| 20 | - String token = "16b4356581984d538652354b60d69ff0"; | ||
| 21 | - this.sunoConfig = new SunoConfig(token); | 20 | + String url = "https://suno-ix9nve79x-status2xxs-projects.vercel.app"; |
| 21 | + this.sunoApi = new SunoApi(new SunoConfig(url)); | ||
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | @Test | 24 | @Test |
| 25 | public void selectById() { | 25 | public void selectById() { |
| 26 | - SunoApi sunoApi = new SunoApi(); | ||
| 27 | System.out.println(sunoApi.selectById("d460ddda-7c87-4f34-b751-419b08a590ca,ff90ea66-49cd-4fd2-b44c-44267dfd5551")); | 26 | System.out.println(sunoApi.selectById("d460ddda-7c87-4f34-b751-419b08a590ca,ff90ea66-49cd-4fd2-b44c-44267dfd5551")); |
| 28 | 27 | ||
| 29 | } | 28 | } |
| 30 | 29 | ||
| 31 | @Test | 30 | @Test |
| 32 | public void generate() { | 31 | public void generate() { |
| 33 | - SunoApi sunoApi = new SunoApi(); | ||
| 34 | List<SunoApi.MusicData> generate = sunoApi.generate(new SunoApi.SunoReq("创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")); | 32 | List<SunoApi.MusicData> generate = sunoApi.generate(new SunoApi.SunoReq("创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")); |
| 35 | System.out.println(generate); | 33 | System.out.println(generate); |
| 36 | } | 34 | } |
| @@ -38,7 +36,6 @@ public class SunoTests { | @@ -38,7 +36,6 @@ public class SunoTests { | ||
| 38 | 36 | ||
| 39 | @Test | 37 | @Test |
| 40 | public void doChatCompletion() { | 38 | public void doChatCompletion() { |
| 41 | - SunoApi sunoApi = new SunoApi(); | ||
| 42 | List<SunoApi.MusicData> generate = sunoApi.doChatCompletion("创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。"); | 39 | List<SunoApi.MusicData> generate = sunoApi.doChatCompletion("创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。"); |
| 43 | System.out.println(generate); | 40 | System.out.println(generate); |
| 44 | } | 41 | } |
| @@ -46,16 +43,13 @@ public class SunoTests { | @@ -46,16 +43,13 @@ public class SunoTests { | ||
| 46 | 43 | ||
| 47 | @Test | 44 | @Test |
| 48 | public void generateLyrics() { | 45 | public void generateLyrics() { |
| 49 | - SunoApi sunoApi = new SunoApi(); | ||
| 50 | SunoApi.LyricsData lyricsData = sunoApi.generateLyrics("A soothing lullaby"); | 46 | SunoApi.LyricsData lyricsData = sunoApi.generateLyrics("A soothing lullaby"); |
| 51 | System.out.println(lyricsData); | 47 | System.out.println(lyricsData); |
| 52 | } | 48 | } |
| 53 | 49 | ||
| 54 | 50 | ||
| 55 | - | ||
| 56 | @Test | 51 | @Test |
| 57 | public void selectLimit() { | 52 | public void selectLimit() { |
| 58 | - SunoApi sunoApi = new SunoApi(); | ||
| 59 | SunoApi.LimitData limitData = sunoApi.selectLimit(); | 53 | SunoApi.LimitData limitData = sunoApi.selectLimit(); |
| 60 | System.out.println(limitData); | 54 | System.out.println(limitData); |
| 61 | } | 55 | } |
yudao-server/src/main/resources/application.yaml
| @@ -201,7 +201,7 @@ yudao.ai: | @@ -201,7 +201,7 @@ yudao.ai: | ||
| 201 | channel-id: 1237948819677904960 | 201 | channel-id: 1237948819677904960 |
| 202 | suno: | 202 | suno: |
| 203 | enable: true | 203 | enable: true |
| 204 | - token: 16b4356581984d538652354b60d69ff0 | 204 | + base-url: https://suno-ix9nve79x-status2xxs-projects.vercel.app |
| 205 | 205 | ||
| 206 | --- #################### 芋道相关配置 #################### | 206 | --- #################### 芋道相关配置 #################### |
| 207 | 207 |