Commit 851c290c0d3240822272757cc0ef940fb2ebfb47
1 parent
c0de6cc5
【增加】AI:使用suno-api服务接入Suno
Showing
7 changed files
with
307 additions
and
80 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/MusicDataVO.java
| 1 | 1 | package cn.iocoder.yudao.module.ai.controller.admin.music.vo; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; | |
| 3 | +import cn.iocoder.yudao.framework.ai.core.model.suno.api.AceDataSunoApi; | |
| 4 | 4 | import com.fasterxml.jackson.annotation.JsonProperty; |
| 5 | 5 | import lombok.Data; |
| 6 | 6 | |
| ... | ... | @@ -66,7 +66,7 @@ public class MusicDataVO { |
| 66 | 66 | */ |
| 67 | 67 | private String style; |
| 68 | 68 | |
| 69 | - public static List<MusicDataVO> convertFrom(List<SunoApi.SunoResp.MusicData> musicDataList) { | |
| 69 | + public static List<MusicDataVO> convertFrom(List<AceDataSunoApi.SunoResp.MusicData> musicDataList) { | |
| 70 | 70 | return musicDataList.stream().map(musicData -> { |
| 71 | 71 | MusicDataVO musicDataVO = new MusicDataVO(); |
| 72 | 72 | musicDataVO.setId(musicData.id()); | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/SunoRespVO.java
| 1 | 1 | package cn.iocoder.yudao.module.ai.controller.admin.music.vo; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; | |
| 3 | +import cn.iocoder.yudao.framework.ai.core.model.suno.api.AceDataSunoApi; | |
| 4 | 4 | import com.fasterxml.jackson.annotation.JsonProperty; |
| 5 | 5 | import lombok.Data; |
| 6 | 6 | |
| ... | ... | @@ -29,7 +29,7 @@ public class SunoRespVO { |
| 29 | 29 | |
| 30 | 30 | |
| 31 | 31 | //把 SunoResp转为本vo类 |
| 32 | - public static SunoRespVO convertFrom(SunoApi.SunoResp sunoResp) { | |
| 32 | + public static SunoRespVO convertFrom(AceDataSunoApi.SunoResp sunoResp) { | |
| 33 | 33 | SunoRespVO sunoRespVO = new SunoRespVO(); |
| 34 | 34 | sunoRespVO.setSuccess(sunoResp.success()); |
| 35 | 35 | sunoRespVO.setTaskId(sunoResp.taskId()); | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/MusicServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.ai.service.music; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; | |
| 3 | +import cn.iocoder.yudao.framework.ai.core.model.suno.api.AceDataSunoApi; | |
| 4 | 4 | import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoReqVO; |
| 5 | 5 | import cn.iocoder.yudao.module.ai.controller.admin.music.vo.SunoRespVO; |
| 6 | 6 | import lombok.RequiredArgsConstructor; |
| ... | ... | @@ -14,11 +14,11 @@ import org.springframework.stereotype.Service; |
| 14 | 14 | @RequiredArgsConstructor |
| 15 | 15 | public class MusicServiceImpl implements MusicService { |
| 16 | 16 | |
| 17 | - private final SunoApi sunoApi; | |
| 17 | + private final AceDataSunoApi aceDataSunoApi; | |
| 18 | 18 | |
| 19 | 19 | @Override |
| 20 | 20 | public SunoRespVO musicGen(SunoReqVO sunoReqVO) { |
| 21 | - SunoApi.SunoResp sunoResp = sunoApi.musicGen(new SunoApi.SunoReq( | |
| 21 | + AceDataSunoApi.SunoResp sunoResp = aceDataSunoApi.musicGen(new AceDataSunoApi.SunoReq( | |
| 22 | 22 | sunoReqVO.getPrompt(), |
| 23 | 23 | sunoReqVO.getLyric(), |
| 24 | 24 | sunoReqVO.isCustom(), | ... | ... |
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 | 4 | import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactory; |
| 5 | 5 | import cn.iocoder.yudao.framework.ai.core.factory.AiClientFactoryImpl; |
| 6 | 6 | import cn.iocoder.yudao.framework.ai.core.model.suno.SunoConfig; |
| 7 | -import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; | |
| 7 | +import cn.iocoder.yudao.framework.ai.core.model.suno.api.AceDataSunoApi; | |
| 8 | 8 | import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenChatClient; |
| 9 | 9 | import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenChatModal; |
| 10 | 10 | import cn.iocoder.yudao.framework.ai.core.model.tongyi.QianWenOptions; |
| ... | ... | @@ -150,8 +150,8 @@ public class YudaoAiAutoConfiguration { |
| 150 | 150 | |
| 151 | 151 | @Bean |
| 152 | 152 | @ConditionalOnProperty(value = "yudao.ai.suno.enable", havingValue = "true") |
| 153 | - public SunoApi sunoApi(YudaoAiProperties yudaoAiProperties) { | |
| 154 | - return new SunoApi(new SunoConfig(yudaoAiProperties.getSuno().getToken())); | |
| 153 | + public AceDataSunoApi sunoApi(YudaoAiProperties yudaoAiProperties) { | |
| 154 | + return new AceDataSunoApi(new SunoConfig(yudaoAiProperties.getSuno().getToken())); | |
| 155 | 155 | } |
| 156 | 156 | |
| 157 | 157 | private static @NotNull MidjourneyConfig getMidjourneyConfig(ApplicationContext applicationContext, | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/suno/api/AceDataSunoApi.java
0 → 100644
| 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 | 1 | package cn.iocoder.yudao.framework.ai.core.model.suno.api; |
| 2 | 2 | |
| 3 | -import cn.iocoder.yudao.framework.ai.core.model.suno.SunoConfig; | |
| 4 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; |
| 5 | 4 | import com.fasterxml.jackson.annotation.JsonProperty; |
| 6 | 5 | import lombok.extern.slf4j.Slf4j; |
| 7 | -import org.springframework.ai.openai.api.ApiUtils; | |
| 6 | +import org.springframework.core.ParameterizedTypeReference; | |
| 7 | +import org.springframework.http.HttpStatusCode; | |
| 8 | +import org.springframework.http.MediaType; | |
| 9 | +import org.springframework.web.reactive.function.client.ClientResponse; | |
| 8 | 10 | import org.springframework.web.reactive.function.client.WebClient; |
| 9 | 11 | import reactor.core.publisher.Mono; |
| 10 | 12 | |
| 11 | 13 | import java.util.List; |
| 14 | +import java.util.function.Function; | |
| 15 | +import java.util.function.Predicate; | |
| 12 | 16 | |
| 13 | 17 | /** |
| 14 | 18 | * Suno API |
| 15 | 19 | * <br> |
| 16 | - * 文档地址:https://platform.acedata.cloud/documents/d016ee3f-421b-4b6e-989a-8beba8701701 | |
| 20 | + * 文档地址:https://github.com/status2xx/suno-api/blob/main/README_CN.md | |
| 17 | 21 | * |
| 18 | 22 | * @Author xiaoxin |
| 19 | - * @Date 2024/5/27 | |
| 23 | + * @Date 2024/6/3 | |
| 20 | 24 | */ |
| 21 | 25 | @Slf4j |
| 22 | 26 | public class SunoApi { |
| 23 | 27 | |
| 24 | - public static final String DEFAULT_BASE_URL = "https://api.acedata.cloud/suno"; | |
| 28 | + public static final String DEFAULT_BASE_URL = "https://suno-9323szg26-status2xxs-projects.vercel.app"; | |
| 25 | 29 | private final WebClient webClient; |
| 26 | 30 | |
| 27 | - public SunoApi(SunoConfig config) { | |
| 31 | + | |
| 32 | + private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful(); | |
| 33 | + private final Function<ClientResponse, Mono<? extends Throwable>> EXCEPTION_FUNCTION = response -> response.bodyToMono(String.class) | |
| 34 | + .handle((respBody, sink) -> { | |
| 35 | + log.error("【suno-api】调用失败!resp: 【{}】", respBody); | |
| 36 | + sink.error(new IllegalStateException("【suno-api】调用失败!")); | |
| 37 | + }); | |
| 38 | + | |
| 39 | + | |
| 40 | + public SunoApi() { | |
| 28 | 41 | this.webClient = WebClient.builder() |
| 29 | 42 | .baseUrl(DEFAULT_BASE_URL) |
| 30 | - .defaultHeaders(ApiUtils.getJsonContentHeaders(config.getToken())) | |
| 43 | + .defaultHeaders((headers) -> headers.setContentType(MediaType.APPLICATION_JSON)) | |
| 31 | 44 | .build(); |
| 32 | 45 | } |
| 33 | 46 | |
| 34 | - // TODO @芋艿:方法名,要考虑下; | |
| 35 | - public SunoResp musicGen(SunoReq sunReq) { | |
| 47 | + public List<MusicData> generate(SunoApi.SunoReq sunReq) { | |
| 48 | + return this.webClient.post() | |
| 49 | + .uri("/api/generate") | |
| 50 | + .body(Mono.just(sunReq), SunoApi.SunoReq.class) | |
| 51 | + .retrieve() | |
| 52 | + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) | |
| 53 | + .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { | |
| 54 | + }) | |
| 55 | + .block(); | |
| 56 | + } | |
| 57 | + | |
| 58 | + public List<MusicData> doChatCompletion(String prompt) { | |
| 59 | + return this.webClient.post() | |
| 60 | + .uri("/v1/chat/completions") | |
| 61 | + .body(Mono.just(new SunoReq(prompt)), SunoApi.SunoReq.class) | |
| 62 | + .retrieve() | |
| 63 | + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) | |
| 64 | + .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { | |
| 65 | + }) | |
| 66 | + .block(); | |
| 67 | + } | |
| 68 | + | |
| 69 | + public LyricsData generateLyrics(String prompt) { | |
| 36 | 70 | return this.webClient.post() |
| 37 | - .uri("/audios") | |
| 38 | - .body(Mono.just(sunReq), SunoReq.class) | |
| 71 | + .uri("/api/generate_lyrics") | |
| 72 | + .body(Mono.just(new SunoReq(prompt)), SunoApi.SunoReq.class) | |
| 73 | + .retrieve() | |
| 74 | + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) | |
| 75 | + .bodyToMono(LyricsData.class) | |
| 76 | + .block(); | |
| 77 | + } | |
| 78 | + | |
| 79 | + | |
| 80 | + public List<MusicData> selectById(String ids) { | |
| 81 | + return this.webClient.get() | |
| 82 | + .uri(uriBuilder -> uriBuilder | |
| 83 | + .path("/api/get") | |
| 84 | + .queryParam("ids", ids) | |
| 85 | + .build()) | |
| 39 | 86 | .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) | |
| 87 | + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) | |
| 88 | + .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { | |
| 89 | + }) | |
| 47 | 90 | .block(); |
| 48 | 91 | } |
| 49 | 92 | |
| 93 | + | |
| 94 | + public LimitData selectLimit() { | |
| 95 | + return this.webClient.get() | |
| 96 | + .uri("/api/get_limit") | |
| 97 | + .retrieve() | |
| 98 | + .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) | |
| 99 | + .bodyToMono(LimitData.class) | |
| 100 | + .block(); | |
| 101 | + } | |
| 102 | + | |
| 103 | + | |
| 50 | 104 | /** |
| 51 | - * 请求数据对象,用于生成音乐音频。 | |
| 105 | + * 根据提示生成音频 | |
| 52 | 106 | * |
| 53 | - * @param prompt 用于生成音乐音频的提示 | |
| 54 | - * @param lyric 用于生成音乐音频的歌词 | |
| 55 | - * @param custom 指示音乐音频是否为定制,如果为 true,则从歌词生成,否则从提示生成 | |
| 56 | - * @param title 音乐音频的标题 | |
| 57 | - * @param style 音乐音频的风格 | |
| 58 | - * @param callbackUrl 音乐音频生成后回调的 URL | |
| 107 | + * @param prompt 用于生成音乐音频的提示 | |
| 108 | + * @param tags 自定义模式才需要 | |
| 109 | + * @param title 自定义模式才需要 | |
| 110 | + * @param waitAudio false表示后台模式,仅返回音频任务信息,需要调用get API获取详细的音频信息。 | |
| 111 | + * true表示同步模式,API最多等待100s,音频生成完毕后直接返回音频链接等信息,建议在GPT等agent中使用。 | |
| 112 | + * @param makeInstrumental 指示音乐音频是否为定制,如果为 true,则从歌词生成,否则从提示生成 | |
| 59 | 113 | */ |
| 60 | 114 | @JsonInclude(value = JsonInclude.Include.NON_NULL) |
| 61 | 115 | public record SunoReq( |
| 62 | 116 | String prompt, |
| 63 | - String lyric, | |
| 64 | - boolean custom, | |
| 117 | + String tags, | |
| 65 | 118 | String title, |
| 66 | - String style, | |
| 67 | - String callbackUrl | |
| 119 | + @JsonProperty("wait_audio") boolean waitAudio, | |
| 120 | + @JsonProperty("make_instrumental") boolean makeInstrumental | |
| 68 | 121 | ) { |
| 69 | 122 | public SunoReq(String prompt) { |
| 70 | - this(prompt, null, false, null, null, null); | |
| 123 | + this(prompt, null, null, false, false); | |
| 71 | 124 | } |
| 72 | 125 | |
| 126 | + public SunoReq(String prompt, String tags, String title) { | |
| 127 | + this(prompt, tags, title, false, false); | |
| 128 | + } | |
| 73 | 129 | } |
| 74 | 130 | |
| 131 | + | |
| 75 | 132 | /** |
| 76 | - * SunoAPI 响应的数据。 | |
| 133 | + * SunoAPI 响应的音频数据。 | |
| 77 | 134 | * |
| 78 | - * @param success 表示请求是否成功 | |
| 79 | - * @param taskId 任务 ID | |
| 80 | - * @param data 音乐数据列表 | |
| 135 | + * @param id 音乐数据的 ID | |
| 136 | + * @param title 音乐音频的标题 | |
| 137 | + * @param imageUrl 音乐音频的图片 URL | |
| 138 | + * @param lyric 音乐音频的歌词 | |
| 139 | + * @param audioUrl 音乐音频的 URL | |
| 140 | + * @param videoUrl 音乐视频的 URL | |
| 141 | + * @param createdAt 音乐音频的创建时间 | |
| 142 | + * @param modelName | |
| 143 | + * @param status | |
| 144 | + * @param gptDescriptionPrompt | |
| 145 | + * @param prompt 生成音乐音频的提示 | |
| 146 | + * @param type | |
| 147 | + * @param tags | |
| 81 | 148 | */ |
| 82 | - public record SunoResp( | |
| 83 | - boolean success, | |
| 84 | - @JsonProperty("task_id") String taskId, | |
| 85 | - List<MusicData> data | |
| 149 | + public record MusicData( | |
| 150 | + String id, | |
| 151 | + String title, | |
| 152 | + @JsonProperty("image_url") String imageUrl, | |
| 153 | + String lyric, | |
| 154 | + @JsonProperty("audio_url") String audioUrl, | |
| 155 | + @JsonProperty("video_url") String videoUrl, | |
| 156 | + @JsonProperty("created_at") String createdAt, | |
| 157 | + @JsonProperty("model_name") String modelName, | |
| 158 | + String status, | |
| 159 | + @JsonProperty("gpt_description_prompt") String gptDescriptionPrompt, | |
| 160 | + String prompt, | |
| 161 | + String type, | |
| 162 | + String tags | |
| 86 | 163 | ) { |
| 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 | 164 | } |
| 165 | + | |
| 166 | + | |
| 167 | + /** | |
| 168 | + * SunoAPI 响应的歌词数据。 | |
| 169 | + * | |
| 170 | + * @param text 歌词 | |
| 171 | + * @param title 标题 | |
| 172 | + * @param status 状态 | |
| 173 | + */ | |
| 174 | + public record LyricsData( | |
| 175 | + String text, | |
| 176 | + String title, | |
| 177 | + String status | |
| 178 | + ) { | |
| 179 | + } | |
| 180 | + | |
| 181 | + | |
| 182 | + /** | |
| 183 | + * SunoAPI 响应的限额数据,目前每日免费50 | |
| 184 | + */ | |
| 185 | + public record LimitData( | |
| 186 | + @JsonProperty("credits_left") Long creditsLeft, | |
| 187 | + String period, | |
| 188 | + @JsonProperty("monthly_limit") Long monthlyLimit, | |
| 189 | + @JsonProperty("monthly_usage") Long monthlyUsage | |
| 190 | + ) { | |
| 191 | + } | |
| 192 | + | |
| 193 | + | |
| 115 | 194 | } | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/suno/SunoTests.java
| ... | ... | @@ -5,6 +5,8 @@ import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; |
| 5 | 5 | import org.junit.Before; |
| 6 | 6 | import org.junit.Test; |
| 7 | 7 | |
| 8 | +import java.util.List; | |
| 9 | + | |
| 8 | 10 | /** |
| 9 | 11 | * @Author xiaoxin |
| 10 | 12 | * @Date 2024/5/27 |
| ... | ... | @@ -20,12 +22,43 @@ public class SunoTests { |
| 20 | 22 | } |
| 21 | 23 | |
| 22 | 24 | @Test |
| 23 | - public void generateMusic() { | |
| 24 | - SunoApi sunoApi = new SunoApi(sunoConfig); | |
| 25 | - SunoApi.SunoReq sunoReq = new SunoApi.SunoReq("创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。"); | |
| 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")); | |
| 28 | + | |
| 29 | + } | |
| 30 | + | |
| 31 | + @Test | |
| 32 | + public void generate() { | |
| 33 | + SunoApi sunoApi = new SunoApi(); | |
| 34 | + List<SunoApi.MusicData> generate = sunoApi.generate(new SunoApi.SunoReq("创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")); | |
| 35 | + System.out.println(generate); | |
| 36 | + } | |
| 37 | + | |
| 38 | + | |
| 39 | + @Test | |
| 40 | + public void doChatCompletion() { | |
| 41 | + SunoApi sunoApi = new SunoApi(); | |
| 42 | + List<SunoApi.MusicData> generate = sunoApi.doChatCompletion("创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。"); | |
| 43 | + System.out.println(generate); | |
| 44 | + } | |
| 45 | + | |
| 46 | + | |
| 47 | + @Test | |
| 48 | + public void generateLyrics() { | |
| 49 | + SunoApi sunoApi = new SunoApi(); | |
| 50 | + SunoApi.LyricsData lyricsData = sunoApi.generateLyrics("A soothing lullaby"); | |
| 51 | + System.out.println(lyricsData); | |
| 52 | + } | |
| 26 | 53 | |
| 27 | - SunoApi.SunoResp sunoResp = sunoApi.musicGen(sunoReq); | |
| 28 | - System.out.println(sunoResp); | |
| 54 | + | |
| 55 | + | |
| 56 | + @Test | |
| 57 | + public void selectLimit() { | |
| 58 | + SunoApi sunoApi = new SunoApi(); | |
| 59 | + SunoApi.LimitData limitData = sunoApi.selectLimit(); | |
| 60 | + System.out.println(limitData); | |
| 29 | 61 | } |
| 30 | 62 | |
| 63 | + | |
| 31 | 64 | } | ... | ... |