mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-23 07:41:53 +08:00
!8 【解决todo】AI 音乐:1.处理失败任务 2.两种生成类型提示词合并,库中区分
Merge pull request !8 from 小新/master-jdk21-ai
This commit is contained in:
commit
3f0d823a85
@ -16,7 +16,8 @@ import java.util.Arrays;
|
||||
public enum AiMusicStatusEnum implements IntArrayValuable {
|
||||
|
||||
IN_PROGRESS(10, "进行中"),
|
||||
SUCCESS(20, "已完成");
|
||||
SUCCESS(20, "已完成"),
|
||||
FAIL(30, "已失败");
|
||||
|
||||
/**
|
||||
* 状态
|
||||
|
@ -38,10 +38,6 @@ public class AiMusicController {
|
||||
@PostMapping("/generate")
|
||||
@Operation(summary = "音乐生成")
|
||||
public CommonResult<List<Long>> generateMusic(@RequestBody @Valid AiSunoGenerateReqVO reqVO) {
|
||||
if (true) {
|
||||
musicService.syncMusic();
|
||||
return null;
|
||||
}
|
||||
return success(musicService.generateMusic(getLoginUserId(), reqVO));
|
||||
}
|
||||
|
||||
@ -64,12 +60,11 @@ public class AiMusicController {
|
||||
return success(BeanUtils.toBean(music, AiMusicRespVO.class));
|
||||
}
|
||||
|
||||
// TODO @xin:这个搞成 updateMy ,修改【我的】音乐。方便后续支持其它字段;另外,需要校验下,更新的音乐,是不是我的!
|
||||
@PostMapping("/updateTitle-my")
|
||||
@PostMapping("/update-my")
|
||||
@Operation(summary = "修改【我的】音乐 目前只支持修改标题")
|
||||
@Parameter(name = "title", required = true, description = "音乐名称", example = "夜空中最亮的星")
|
||||
public CommonResult<Boolean> updateMusicTitle(AiMusicUpdateTitleReqVO updateReqVO) {
|
||||
musicService.updateMusicTitle(updateReqVO);
|
||||
public CommonResult<Boolean> updateMy(AiMusicUpdateReqVO updateReqVO) {
|
||||
musicService.updateMyMusic(updateReqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
|
@ -15,4 +15,7 @@ public class AiMusicUpdateReqVO {
|
||||
@Schema(description = "是否发布", example = "true")
|
||||
private Boolean publicStatus;
|
||||
|
||||
@Schema(description = "音乐名称", example = "夜空中最亮的星")
|
||||
private String title;
|
||||
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
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;
|
||||
|
||||
}
|
@ -24,10 +24,10 @@ public class AiSunoGenerateReqVO {
|
||||
@NotNull(message = "生成模式不能为空")
|
||||
private Integer generateMode; // 参见 AiMusicGenerateModeEnum 枚举
|
||||
|
||||
// TODO @xin:方案一:prompt => lyric 歌词;gptDescriptionPrompt => description 描述(db 那字段也改下,避免和 gpt 直接耦合);这样搞完后,会更统一好理解一点
|
||||
// TODO @xin:方案二:还是之前的做法,都用 prompt;不过最终 gptDescriptionPrompt 还是存储 description 算描述。可以微信一起讨论下。
|
||||
@Schema(description = "用于生成音乐音频的歌词提示",
|
||||
example = """
|
||||
1.描述模式:创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。
|
||||
2.歌词模式:
|
||||
[Verse]
|
||||
阳光下奔跑 多么欢快
|
||||
假期就要来 心都飞起来
|
||||
@ -39,11 +39,7 @@ public class AiSunoGenerateReqVO {
|
||||
日子太短暂 别再等待
|
||||
马上放假了 梦想起飞
|
||||
""")
|
||||
private String prompt; // 歌词模式用
|
||||
|
||||
@Schema(description = "用于生成音乐音频的描述",
|
||||
example = "创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")
|
||||
private String gptDescriptionPrompt; // 描述模式用
|
||||
private String prompt;
|
||||
|
||||
@Schema(description = "是否纯音乐", example = "true")
|
||||
private Boolean makeInstrumental;
|
||||
|
@ -75,11 +75,7 @@ public class AiMusicDO extends BaseDO {
|
||||
/**
|
||||
* 描述词
|
||||
*/
|
||||
private String gptDescriptionPrompt;
|
||||
/**
|
||||
* 提示词
|
||||
*/
|
||||
private String prompt;
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 平台
|
||||
|
@ -38,11 +38,11 @@ public interface AiMusicService {
|
||||
void updateMusic(@Valid AiMusicUpdateReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 更新音乐名称
|
||||
* 更新我的音乐
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updateMusicTitle(@Valid AiMusicUpdateTitleReqVO updateReqVO);
|
||||
void updateMyMusic(@Valid AiMusicUpdateReqVO updateReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 删除AI 音乐
|
||||
|
@ -3,11 +3,14 @@ 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.ObjectUtil;
|
||||
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.*;
|
||||
import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
|
||||
import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
|
||||
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;
|
||||
@ -48,18 +51,18 @@ public class AiMusicServiceImpl implements AiMusicService {
|
||||
public List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO) {
|
||||
// 1. 调用 Suno 生成音乐
|
||||
SunoApi sunoApi = apiKeyService.getSunoApi();
|
||||
// TODO @xin:这两个貌似一直没跑成功,你那可以么?用的请求是 AiMusicController.http 的
|
||||
// TODO 芋艿:这两个貌似一直没跑成功,你那可以么?用的请求是 AiMusicController.http 的 --xin:大部分ok的,补充了error_message
|
||||
List<SunoApi.MusicData> musicDataList;
|
||||
if (Objects.equals(AiMusicGenerateModeEnum.LYRIC.getMode(), reqVO.getGenerateMode())) {
|
||||
// 1.1 歌词模式
|
||||
if (Objects.equals(AiMusicGenerateModeEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) {
|
||||
// 1.1 描述模式
|
||||
SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
|
||||
reqVO.getPrompt(), reqVO.getModel(), reqVO.getMakeInstrumental());
|
||||
musicDataList = sunoApi.generate(generateRequest);
|
||||
} else if (Objects.equals(AiMusicGenerateModeEnum.LYRIC.getMode(), reqVO.getGenerateMode())) {
|
||||
// 1.2 歌词模式
|
||||
SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
|
||||
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.getGptDescriptionPrompt(), reqVO.getModel(), reqVO.getMakeInstrumental());
|
||||
musicDataList = sunoApi.generate(generateRequest);
|
||||
} else {
|
||||
throw new IllegalArgumentException(StrUtil.format("未知生成模式({})", reqVO));
|
||||
}
|
||||
@ -108,9 +111,12 @@ public class AiMusicServiceImpl implements AiMusicService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMusicTitle(AiMusicUpdateTitleReqVO updateReqVO) {
|
||||
// 校验存在
|
||||
validateMusicExists(updateReqVO.getId());
|
||||
public void updateMyMusic(AiMusicUpdateReqVO updateReqVO, Long userId) {
|
||||
// 校验音乐是否存在
|
||||
AiMusicDO musicDO = validateMusicExists(updateReqVO.getId());
|
||||
if (ObjUtil.notEqual(musicDO.getUserId(), userId)) {
|
||||
throw exception(MUSIC_NOT_EXISTS);
|
||||
}
|
||||
// 更新
|
||||
musicMapper.updateById(new AiMusicDO().setId(updateReqVO.getId()).setTitle(updateReqVO.getTitle()));
|
||||
}
|
||||
@ -156,29 +162,38 @@ public class AiMusicServiceImpl implements AiMusicService {
|
||||
* @return AiMusicDO 集合
|
||||
*/
|
||||
private List<AiMusicDO> buildMusicDOList(List<SunoApi.MusicData> musicList) {
|
||||
// TODO @xin:它有 status = error 状态,表示失败噢。
|
||||
return convertList(musicList, musicData -> new AiMusicDO()
|
||||
.setTaskId(musicData.id()).setModel(musicData.modelName())
|
||||
.setPrompt(musicData.prompt()).setGptDescriptionPrompt(musicData.gptDescriptionPrompt())
|
||||
// TODO @xin:只有在完成的状态,在下载文件
|
||||
.setAudioUrl(downloadFile(musicData.audioUrl()))
|
||||
.setVideoUrl(downloadFile(musicData.videoUrl()))
|
||||
.setImageUrl(downloadFile(musicData.imageUrl()))
|
||||
.setTitle(musicData.title()).setDuration(musicData.duration())
|
||||
.setLyric(musicData.lyric()).setTags(StrUtil.split(musicData.tags(), StrPool.COMMA))
|
||||
.setStatus(Objects.equals("complete", musicData.status()) ?
|
||||
AiMusicStatusEnum.SUCCESS.getStatus() : AiMusicStatusEnum.IN_PROGRESS.getStatus()));
|
||||
return convertList(musicList, musicData -> {
|
||||
Integer status;
|
||||
if (Objects.equals("complete", musicData.status())) {
|
||||
status = AiMusicStatusEnum.SUCCESS.getStatus();
|
||||
} else if (Objects.equals("error", musicData.status())) {
|
||||
status = AiMusicStatusEnum.FAIL.getStatus();
|
||||
} else {
|
||||
status = AiMusicStatusEnum.IN_PROGRESS.getStatus();
|
||||
}
|
||||
return new AiMusicDO()
|
||||
.setTaskId(musicData.id()).setModel(musicData.modelName())
|
||||
.setDescription(musicData.gptDescriptionPrompt())
|
||||
.setAudioUrl(downloadFile(status, musicData.audioUrl()))
|
||||
.setVideoUrl(downloadFile(status, musicData.videoUrl()))
|
||||
.setImageUrl(downloadFile(status, musicData.imageUrl()))
|
||||
.setTitle(musicData.title()).setDuration(musicData.duration())
|
||||
.setLyric(musicData.lyric()).setTags(StrUtil.split(musicData.tags(), StrPool.COMMA))
|
||||
.setErrorMessage(musicData.errorMessage())
|
||||
.setStatus(status);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 将生成的音频文件上传到文件服务器
|
||||
* 音乐生成好后,将音频文件上传到文件服务器
|
||||
*
|
||||
* @param url 音频文件地址
|
||||
* @param status 音乐状态
|
||||
* @param url 音频文件地址
|
||||
* @return 内部文件地址
|
||||
*/
|
||||
private String downloadFile(String url) {
|
||||
if (StrUtil.isBlank(url)) {
|
||||
return null;
|
||||
private String downloadFile(Integer status, String url) {
|
||||
if (StrUtil.isBlank(url) || ObjectUtil.notEqual(status, AiMusicStatusEnum.SUCCESS.getStatus())) {
|
||||
return url;
|
||||
}
|
||||
try {
|
||||
byte[] bytes = HttpUtil.downloadBytes(url);
|
||||
|
@ -175,6 +175,7 @@ public class SunoApi {
|
||||
@JsonProperty("model_name") String modelName,
|
||||
String status,
|
||||
@JsonProperty("gpt_description_prompt") String gptDescriptionPrompt,
|
||||
@JsonProperty("error_message") String errorMessage,
|
||||
String prompt,
|
||||
String type,
|
||||
String tags,
|
||||
|
@ -17,8 +17,8 @@ public class SunoTests {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
// String url = "https://suno-om0w1cy6e-status2xxs-projects.vercel.app";
|
||||
String url = "http://127.0.0.1:3001";
|
||||
String url = "https://suno-55ishh05u-status2xxs-projects.vercel.app";
|
||||
// String url = "http://127.0.0.1:3001";
|
||||
this.sunoApi = new SunoApi(url);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user