Commit 9f33b0ca authored by 周成波's avatar 周成波

改一下LLM接口名称

parent fbf3035b
...@@ -4,12 +4,12 @@ ...@@ -4,12 +4,12 @@
import request from '@/api/request' import request from '@/api/request'
export default { export default {
submitGpt(prompt: string, llm: string = "gpt"): Promise<string> { submitLLM(prompt: string, llm: string = "gpt"): Promise<string> {
if (!prompt) { if (!prompt) {
return Promise.reject("输入不能为空"); return Promise.reject("输入不能为空");
} }
const post_data = { source_text: prompt, llm: llm } const post_data = { source_text: prompt, llm: llm }
return request.post('/text2video/text2gpt', post_data) return request.post('/text2video/text2llm', post_data)
.then((res: any) => { .then((res: any) => {
// console.log(res); // console.log(res);
if (res && res.code === 0) { if (res && res.code === 0) {
......
<script setup lang="ts">
import { onMounted, reactive, ref } from "vue";
import { Sunny, UploadFilled } from "@element-plus/icons-vue";
import { ElMessage, genFileId,
type UploadInstance,
type UploadProps,
type UploadRawFile } from "element-plus";
import text2videoService from "@/api/service/text2videoService";
import utils from "@/utils/utils";
import { useManyValues } from './compositions/useManyValues'
const debug = ref(import.meta.env.MODE === 'production' ? false : true);
const loading = ref(false);
const dialogVisible = ref(false);
const dialogData = ref("");
const default_data = useManyValues();
const form = reactive({
screen: default_data.screen,
if_need_subtitle: default_data.if_need_subtitle,
chatgpt_prompt: "",
chatgpt_answer: "",
chatgpt_answer_roles: <Wm.RolesItem[]>[],
adapt_result_json: <Wm.ScriptsItem[]>[],
task_id: "",
final_video: "",
});
const sd_prompt_prefix = default_data.sd_prompt_prefix;
const sd_negative_prompt_prefix = default_data.sd_negative_prompt_prefix;
const wenan_llm = "tyqw"
const wenan_llm_name = "通义千问"
const role_llm = "tyqw"
const role_llm_name = "通义千问"
const tuili_llm = "langchain"
const tuili_llm_name = "baichuan2-7b"
const fanyi_llm = "langchain"
const fanyi_llm_name = "baichuan2-7b"
onMounted(() => {
// 初始化示例数据
onChangeScreen(form.screen);
});
const onSubmitGpt = () => {
text2videoService
.submitGpt(form.chatgpt_prompt, wenan_llm)
.then((result: string) => {
console.log(form.chatgpt_prompt);
console.log(result);
form.chatgpt_answer = result;
})
.catch((error: any) => {
// console.error(error);
ElMessage({
message: error,
type: "error",
});
});
};
const onAdaptRoles = async () => {
if (!form.chatgpt_answer || form.chatgpt_answer.length == 0) {
ElMessage({
message: "文案不能为空",
type: "error",
});
return;
}
loading.value = true;
// 推理角色
try {
const adapt_restrict = `
指令:
请理解这个故事,给出这个故事的所有角色、角色关键词(性别(可以发挥想象进行补充,但一定要明确),年龄(可以发挥想象进行补充,但一定要明确),
肤色(可以发挥想象进行补充,但一定要明确),衣服(可以发挥想象进行补充,但一定要明确),发型(可以发挥想象进行补充,但一定要明确),发色(可以发挥想象进行补充,但一定要明确),
脸色(可以发挥想象进行补充,但一定要明确),五官特点(可以发挥想象进行补充,但一定要明确))。\n
要求:
角色和角色关键词要对应。
严格以如下格式返回:[{"角色":"","角色关键词":""}]`;
const keywords = await text2videoService.submitGpt(form.chatgpt_answer + "\n" + adapt_restrict, role_llm);
console.log(keywords)
const keywords_obj = utils.formatJsonObj(keywords.replace(/```json/g, '').replace(/```/g, ''))
console.log(keywords_obj)
form.chatgpt_answer_roles = []
if ('error' in keywords_obj) {
ElMessage({
message: "未解析到角色",
type: "error",
});
} else {
for (let item of keywords_obj) {
let newObjItem = {
"角色": JSON.stringify(item["角色"]).replace(/"/g, ''),
"角色关键词": JSON.stringify(item["角色关键词"]).replace(/"/g, '')
};
form.chatgpt_answer_roles.push(newObjItem);
}
console.log(form.chatgpt_answer_roles)
}
} catch (error) {
ElMessage({
message: String(error),
type: "error",
});
} finally {
// 最终关闭loading(无论成功或失败)
loading.value = false;
}
};
const onAdapt = async () => {
if (!form.chatgpt_answer || form.chatgpt_answer.length == 0) {
ElMessage({
message: "文案不能为空",
type: "error",
});
return;
}
loading.value = true;
form.task_id = utils.genDateTimeStr();
console.log(form.task_id)
// 按标点拆分成分镜
const sentences = utils.splitText(form.chatgpt_answer);
console.log(sentences.length)
// 分镜
form.adapt_result_json = []
for (let i = 0; i < sentences.length; i++) {
form.adapt_result_json.push({
"编号": (i + 1).toString(),
"场景描述": sentences[i].trim(),
"场景关键词": "",
"角色": "",
"角色关键词": "",
"画面描述词": "",
"本镜配图": "",
"local_image_path": "",
});
}
console.log(form.adapt_result_json)
const delay = (ms: any) => new Promise(res => setTimeout(res, ms));
async function processScenes() {
for (const item of form.adapt_result_json) {
await onAdaptOne(item);
await delay(100);
await onDrawOne(item);
}
}
try {
await processScenes();
ElMessage({
message: "all scene ok",
type: "success"
});
console.log(form.adapt_result_json);
} catch (error) {
ElMessage({
message: String(error),
type: "error"
});
} finally {
loading.value = false; // 最终关闭loading(无论成功或失败)
}
};
const onAdaptOne = async (item: any) => {
if (!item.场景描述) {
ElMessage({
message: "分镜场景描述不能为空",
type: "error",
});
return;
}
// 推理关键词
try {
const adapt_restrict = `
指令:
请理解这个故事,针对其中的这个场景:“${item.场景描述}”,给出这个场景的:
场景关键词(年代,空间,时间段,地理环境,天气,物品,人物,镜头角度)、
角色(从所有角色中选择本场景的角色)。
要求:
角色和角色关键词要对应。
严格以如下格式返回:[{"场景关键词":"(多个词以逗号分隔)","角色":"(角色以逗号分隔)"}]
除了按格式返回的内容之外,不要添加其他的任何说明。`;
const keywords = await text2videoService.submitGpt("故事:\n" + form.chatgpt_answer + "\n所有角色:\n"+ JSON.stringify(form.chatgpt_answer_roles)+"\n" + adapt_restrict, tuili_llm);
// console.log(keywords)
const keywords_obj = utils.formatJsonObj(keywords.replace(/```json/g, '').replace(/```/g, ''))
// console.log(keywords_obj)
if ('error' in keywords_obj) {
ElMessage({
message: `分镜 ${item.编号} 推理关键词失败,请重试`,
type: "error",
});
} else {
item.场景关键词 = JSON.stringify(keywords_obj[0].场景关键词).replace(/"/g, '');
if (form.chatgpt_answer_roles.length === 0) {
// 总角色为空
item.角色 = '';
item.角色关键词 = '';
} else {
// 总角色不为空
// item.角色 = JSON.stringify(keywords_obj[0].角色).replace(/"/g, '');
// item.角色关键词 = JSON.stringify(keywords_obj[0].角色关键词).replace(/"/g, '');
const item_roles = JSON.stringify(keywords_obj[0].角色).replace(/"/g, '');
item.角色 = item_roles;
let role_kws = ""
const item_roles_arr = item_roles.split(/[,,]/);
item_roles_arr.forEach( one_item_role => {
// 人工指定角色关键词,包含则取
form.chatgpt_answer_roles.forEach(i => {
if (i["角色"].includes(one_item_role.trim()) || one_item_role.includes(i["角色"].trim())) {
role_kws = `${role_kws}${i["角色"]}${i["角色关键词"]}】`
}
})
})
item.角色关键词 = role_kws;
}
}
} catch (error) {
ElMessage({
message: String(error),
type: "error",
});
}
};
const onDrawOne = async (item: any) => {
if (!item.场景描述 && !item.场景关键词) {
ElMessage({
message: "场景描述和场景关键词不能都为空",
type: "error",
});
return;
}
// 翻译+画图
if (!form.task_id) {
form.task_id = utils.genDateTimeStr();
console.log(form.task_id)
}
try {
let temp_prompt = ""
if (item.场景描述) {temp_prompt = temp_prompt + `场景描述为:${item.场景描述}\n`};
if (item.场景关键词) {temp_prompt = temp_prompt + `场景关键词为:${item.场景关键词}\n`};
if (item.角色) {temp_prompt = temp_prompt + `场景中的角色有:${item.角色}\n`};
if (item.角色关键词) {temp_prompt = temp_prompt + `角色关键词为:${item.角色关键词}\n`};
const sd_describe = await text2videoService.submitGpt(
`${temp_prompt}
指令:
请理解以上内容,并返回一段英文的描述。`, fanyi_llm
);
item.画面描述词 = sd_describe;
const sd_prompt = item.画面描述词 + "," + sd_prompt_prefix;
let width = "960";
let height = "540";
if (form.screen == "竖屏") {
width = "540";
height = "960";
}
// console.log(sd_prompt);
// console.log(sd_negative_prompt_prefix);
const sd_img = await text2videoService.submitSD(form.task_id, item.编号, sd_prompt, sd_negative_prompt_prefix, width, height);
item.本镜配图 = sd_img.domain_image_path+"?v="+utils.genDateTimeStr();
item.local_image_path = sd_img.local_image_path;
} catch (error) {
ElMessage({
message: String(error),
type: "error",
});
}
};
const onGenVideo = () => {
if (!form.adapt_result_json || form.adapt_result_json.length == 0 ) {
ElMessage({
message: "必要信息不能为空,请重新执行",
type: "error",
});
return;
}
let is_all_ok = true;
form.adapt_result_json.map(item => {
if (item.编号 == "" || item.场景描述 == "" || item.local_image_path == "") {
ElMessage({
message: `分镜 ${item.编号} 的必要信息为空,请重新执行`,
type: "error",
});
is_all_ok = false;
}
});
if (!is_all_ok) return;
console.log(form.adapt_result_json);
const video_param_detail = form.adapt_result_json.map(item => {
return {
idx: item.编号,
text: item.场景描述,
img_path: item.local_image_path
};
});
const video_param = {
task_id: form.task_id,
if_need_subtitle: form.if_need_subtitle,
task_info: video_param_detail,
}
text2videoService
.submitGenVideo(video_param)
.then((result: string) => {
console.log(result);
form.final_video = "";
form.final_video = result+"?v="+utils.genDateTimeStr();
})
.catch((error: any) => {
// console.error(error);
ElMessage({
message: error,
type: "error",
});
});
};
const clean_demo = () => {
form.chatgpt_prompt = "";
form.chatgpt_answer = "";
form.chatgpt_answer_roles = <Wm.RolesItem[]>[];
form.adapt_result_json = <Wm.ScriptsItem[]>[];
form.task_id = "";
form.final_video = "";
}
const clean_roles = () => {
form.chatgpt_answer_roles = <Wm.RolesItem[]>[];
}
const onChangeScreen = (val: string) => {
if (debug.value == true) {
if (val == "横屏") {
form.task_id = default_data.horizontal_data.task_id;
form.chatgpt_prompt = default_data.horizontal_data.chatgpt_prompt;
form.chatgpt_answer = default_data.horizontal_data.chatgpt_answer;
form.chatgpt_answer_roles = default_data.horizontal_data.chatgpt_answer_roles;
form.adapt_result_json = default_data.horizontal_data.adapt_result_json;
form.final_video = default_data.horizontal_data.final_video;
} else {
form.task_id = default_data.vertical_data.task_id;
form.chatgpt_prompt = default_data.vertical_data.chatgpt_prompt;
form.chatgpt_answer = default_data.vertical_data.chatgpt_answer;
form.chatgpt_answer_roles = default_data.vertical_data.chatgpt_answer_roles;
form.adapt_result_json = default_data.vertical_data.adapt_result_json;
form.final_video = default_data.vertical_data.final_video;
}
}
}
const showsdprompt = (item: any) => {
// alert(item.画面描述词)
dialogData.value = item.画面描述词+ "," +sd_prompt_prefix+'===== negative ====='+sd_negative_prompt_prefix;
dialogVisible.value = true; // 打开对话框
}
const upload = ref<UploadInstance>()
const actionUrl = ref(
import.meta.env.MODE === 'production'
? '/file'
: import.meta.env.VITE_APP_BASE_API + '/file'
)
const handleUploadSuccess = (val: Wm.UploadResult) => {
if (val.code == 0){
// console.log(val)
const id = parseInt(val.message) - 1;
form.adapt_result_json[id].本镜配图 = val.data[0].url+"?v="+utils.genDateTimeStr();
form.adapt_result_json[id].local_image_path = val.data[0].path;
ElMessage({
message: '上传成功',
type: 'success'
})
} else {
ElMessage({
message: '上传失败',
type: 'error'
})
}
}
const handleExceed: UploadProps['onExceed'] = (files) => {
upload.value!.clearFiles()
const file = files[0] as UploadRawFile
file.uid = genFileId()
upload.value!.handleStart(file)
upload.value!.submit()
}
</script>
<template>
<main class="home-container">
<!-- 标题 -->
<el-divider content-position="left">text2video</el-divider>
<el-form :model="form" label-width="114px" v-loading="loading">
<el-form-item>
<div>
<el-radio-group v-model="form.screen" @change="onChangeScreen">
<el-radio label="横屏" size="large" border/>
<el-radio label="竖屏" size="large" border/>
</el-radio-group>
</div>
</el-form-item>
<el-form-item>
<el-button type="success" @click="clean_demo">清除所有数据</el-button>
</el-form-item>
<!-- Prompt到文案 -->
<el-form-item label="Prompt">
<el-input v-model="form.chatgpt_prompt" :autosize="true" type="textarea" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmitGpt">生成文案({{wenan_llm_name}}</el-button>
</el-form-item>
<el-form-item label="文案">
<el-input v-model="form.chatgpt_answer" :autosize="true" type="textarea" />
</el-form-item>
<!-- 角色 -->
<el-form-item>
<el-button type="primary" @click="onAdaptRoles">推理所有角色({{role_llm_name}}</el-button>
<el-button plain @click="clean_roles">清空总角色列表</el-button>
</el-form-item>
<el-form-item label="角色">
<el-table :data="form.chatgpt_answer_roles" border style="width: 100%; z-index: calc(var(--el-table-index) -1)">
<el-table-column prop="角色" label="角色">
<template v-slot="scope">
<el-input v-model="scope.row.角色" :autosize="true" type="textarea"></el-input>
</template>
</el-table-column>
<el-table-column prop="角色关键词" label="角色关键词">
<template v-slot="scope">
<el-input v-model="scope.row.角色关键词" :autosize="true" type="textarea"></el-input>
</template>
</el-table-column>
</el-table>
</el-form-item>
<!-- 分镜 -->
<el-form-item>
<el-button type="primary" @click="onAdapt">分镜、推理关键词({{tuili_llm_name}})、翻译({{fanyi_llm_name}})、绘图</el-button>
</el-form-item>
<el-form-item label="分镜">
<el-table :data="form.adapt_result_json" border style="width: 100%; z-index: calc(var(--el-table-index) -1)">
<el-table-column prop="编号" label="编号" width="60" />
<el-table-column prop="场景描述" label="场景描述">
<template v-slot="scope">
<el-input v-model="scope.row.场景描述" :autosize="true" type="textarea"></el-input>
</template>
</el-table-column>
<el-table-column prop="场景关键词" label="场景关键词">
<template v-slot="scope">
<el-input v-model="scope.row.场景关键词" :autosize="true" type="textarea"></el-input>
</template>
</el-table-column>
<el-table-column prop="角色" label="角色">
<template v-slot="scope">
<el-input v-model="scope.row.角色" :autosize="true" type="textarea"></el-input>
</template>
</el-table-column>
<el-table-column prop="角色关键词" label="角色关键词">
<template v-slot="scope">
<el-input v-model="scope.row.角色关键词" :autosize="true" type="textarea"></el-input>
</template>
</el-table-column>
<el-table-column prop="本镜配图" label="本镜配图" width="300">
<template v-slot="scope">
<div>
<el-image :src="scope.row.本镜配图" :zoom-rate="1.2" :max-scale="1.5" :min-scale="0.5"
:preview-src-list="[scope.row.本镜配图]" fit="cover" :hide-on-click-modal="true"
/>
</div>
</template>
</el-table-column>
<el-table-column width="120" label="操作" align="center">
<!--
<template v-slot:header>
<el-button type="danger" size="default" @click="">批量绘制所有图片</el-button>
</template>
-->
<template v-slot="scope">
<div style="margin: 10px 0"><el-button type="primary" size="default" @click="onAdaptOne(scope.row)">推理关键词</el-button></div>
<div style="margin: 10px 0"><el-button type="primary" size="default" @click="onDrawOne(scope.row)">翻译、绘图</el-button></div>
<el-upload
class="upload-demo"
ref="upload"
list-type="picture"
:show-file-list="false"
:limit="1"
:action="actionUrl"
:on-success="handleUploadSuccess"
:on-exceed="handleExceed"
:data="{item_id: scope.row.编号}"
>
<el-button type="primary">上传图片</el-button>
</el-upload>
<div style="margin: 10px 0"><el-button plain @click="showsdprompt(scope.row)">debug</el-button></div>
<el-dialog
v-model=dialogVisible
width="80%"
>
<p>{{ dialogData }}</p>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">ok</el-button>
</div>
</template>
</el-dialog>
</template>
</el-table-column>
</el-table>
</el-form-item>
<!-- 生成视频 -->
<el-form-item>
<el-button type="primary" @click="onGenVideo">生成视频</el-button>
</el-form-item>
<el-form-item>
<video :src="form.final_video" controls></video>
</el-form-item>
</el-form>
</main>
</template>
<style lang="scss" scoped>
.home-container {
width: 100%;
}
</style>
<style lang="scss">
.home-container {
.el-table .el-table__cell {
z-index: calc(var(--el-table-index) -1);
}
}
</style>
...@@ -47,7 +47,7 @@ const fanyi_llm_name = qwen.name ...@@ -47,7 +47,7 @@ const fanyi_llm_name = qwen.name
const voice_rate = ref(-15) const voice_rate = ref(-15)
const voice_volume = ref(0) const voice_volume = ref(0)
const voice = ref("zh-CN-YunjianNeural") const voice = ref("zh-CN-YunjianNeural")
const bgm = ref("") const bgm = ref("解忧曲")
onMounted(() => { onMounted(() => {
...@@ -59,7 +59,7 @@ const delay = (ms: any) => new Promise(res => setTimeout(res, ms)); ...@@ -59,7 +59,7 @@ const delay = (ms: any) => new Promise(res => setTimeout(res, ms));
const onSubmitGpt = () => { const onSubmitGpt = () => {
text2videoService text2videoService
.submitGpt(form.chatgpt_prompt, wenan_llm) .submitLLM(form.chatgpt_prompt, wenan_llm)
.then((result: string) => { .then((result: string) => {
console.log(form.chatgpt_prompt); console.log(form.chatgpt_prompt);
console.log(result); console.log(result);
...@@ -89,7 +89,7 @@ const onAdaptRoles = async () => { ...@@ -89,7 +89,7 @@ const onAdaptRoles = async () => {
const adapt_restrict = ` const adapt_restrict = `
指令: 指令:
请理解这个故事,给出这个故事中的所有角色,多个角色以逗号分隔`; 请理解这个故事,给出这个故事中的所有角色,多个角色以逗号分隔`;
const roles = await text2videoService.submitGpt("故事:\n" + form.chatgpt_answer + "\n" + adapt_restrict, role_llm); const roles = await text2videoService.submitLLM("故事:\n" + form.chatgpt_answer + "\n" + adapt_restrict, role_llm);
form.all_roles = roles.replace(/。/g, '').replace(/、/g, ',') form.all_roles = roles.replace(/。/g, '').replace(/、/g, ',')
console.log(form.all_roles) console.log(form.all_roles)
const roles_arr = form.all_roles.split(/[,,]/); const roles_arr = form.all_roles.split(/[,,]/);
...@@ -106,7 +106,7 @@ const onAdaptRoles = async () => { ...@@ -106,7 +106,7 @@ const onAdaptRoles = async () => {
要求: 要求:
关键词以逗号分隔。 关键词以逗号分隔。
只要返回关键词,不需要其他的说明文字。`; 只要返回关键词,不需要其他的说明文字。`;
let keywords = await text2videoService.submitGpt("故事:\n" + form.chatgpt_answer + "\n" + adapt_keyword_restrict, role_keywords_llm); let keywords = await text2videoService.submitLLM("故事:\n" + form.chatgpt_answer + "\n" + adapt_keyword_restrict, role_keywords_llm);
keywords = keywords.replace(/。/g, '').replace(/、/g, ',') keywords = keywords.replace(/。/g, '').replace(/、/g, ',')
form.chatgpt_answer_roles.push({ form.chatgpt_answer_roles.push({
"角色": one_role.trim(), "角色": one_role.trim(),
...@@ -211,7 +211,7 @@ const onAdaptOne = async (item: any) => { ...@@ -211,7 +211,7 @@ const onAdaptOne = async (item: any) => {
要求: 要求:
关键词以逗号分隔。 关键词以逗号分隔。
只要返回关键词,不需要其他的说明文字。`; 只要返回关键词,不需要其他的说明文字。`;
const keywords = await text2videoService.submitGpt("故事:\n" + form.chatgpt_answer + "\n" + adapt_restrict, tuili_llm); const keywords = await text2videoService.submitLLM("故事:\n" + form.chatgpt_answer + "\n" + adapt_restrict, tuili_llm);
// console.log(keywords) // console.log(keywords)
item.场景关键词 = keywords; item.场景关键词 = keywords;
if (form.chatgpt_answer_roles.length === 0) { if (form.chatgpt_answer_roles.length === 0) {
...@@ -223,7 +223,7 @@ const onAdaptOne = async (item: any) => { ...@@ -223,7 +223,7 @@ const onAdaptOne = async (item: any) => {
const adapt_role_restrict = ` const adapt_role_restrict = `
指令: 指令:
请理解这个故事,针对其中的这个场景:“${item.场景描述}”,从所有角色中选择本场景的角色,多个角色以逗号分隔。`; 请理解这个故事,针对其中的这个场景:“${item.场景描述}”,从所有角色中选择本场景的角色,多个角色以逗号分隔。`;
const item_roles = await text2videoService.submitGpt("故事:\n" + form.chatgpt_answer + "\n所有角色:\n"+ form.all_roles +"\n" + adapt_role_restrict, tuili_llm); const item_roles = await text2videoService.submitLLM("故事:\n" + form.chatgpt_answer + "\n所有角色:\n"+ form.all_roles +"\n" + adapt_role_restrict, tuili_llm);
// console.log(role_keywords) // console.log(role_keywords)
item.角色 = item_roles; item.角色 = item_roles;
let role_kws = "" let role_kws = ""
...@@ -280,7 +280,7 @@ const onDrawOne = async (item: any) => { ...@@ -280,7 +280,7 @@ const onDrawOne = async (item: any) => {
if (item.场景关键词) {temp_prompt = temp_prompt + `场景关键词为:${item.场景关键词}\n`}; if (item.场景关键词) {temp_prompt = temp_prompt + `场景关键词为:${item.场景关键词}\n`};
if (item.角色) {temp_prompt = temp_prompt + `场景中的角色有:${item.角色}\n`}; if (item.角色) {temp_prompt = temp_prompt + `场景中的角色有:${item.角色}\n`};
if (item.角色关键词) {temp_prompt = temp_prompt + `角色关键词为:${item.角色关键词}\n`}; if (item.角色关键词) {temp_prompt = temp_prompt + `角色关键词为:${item.角色关键词}\n`};
const sd_describe = await text2videoService.submitGpt( const sd_describe = await text2videoService.submitLLM(
`${temp_prompt} `${temp_prompt}
指令: 指令:
请理解以上内容,并返回一段英文的描述。`, fanyi_llm 请理解以上内容,并返回一段英文的描述。`, fanyi_llm
......
...@@ -47,7 +47,7 @@ const fanyi_llm_name = gpt.name ...@@ -47,7 +47,7 @@ const fanyi_llm_name = gpt.name
const voice_rate = ref(-15) const voice_rate = ref(-15)
const voice_volume = ref(0) const voice_volume = ref(0)
const voice = ref("en-US-BrianNeural") const voice = ref("en-US-BrianNeural")
const bgm = ref("") const bgm = ref("解忧曲")
onMounted(() => { onMounted(() => {
// 初始化示例数据 // 初始化示例数据
...@@ -58,7 +58,7 @@ const delay = (ms: any) => new Promise(res => setTimeout(res, ms)); ...@@ -58,7 +58,7 @@ const delay = (ms: any) => new Promise(res => setTimeout(res, ms));
const onSubmitGpt = () => { const onSubmitGpt = () => {
text2videoService text2videoService
.submitGpt(form.chatgpt_prompt, wenan_llm) .submitLLM(form.chatgpt_prompt, wenan_llm)
.then((result: string) => { .then((result: string) => {
console.log(form.chatgpt_prompt); console.log(form.chatgpt_prompt);
console.log(result); console.log(result);
...@@ -88,7 +88,7 @@ const onAdaptRoles = async () => { ...@@ -88,7 +88,7 @@ const onAdaptRoles = async () => {
const adapt_restrict = ` const adapt_restrict = `
Instructions: Instructions:
Please understand this story and provide all the characters in it, with multiple characters separated by commas`; Please understand this story and provide all the characters in it, with multiple characters separated by commas`;
const roles = await text2videoService.submitGpt("story:\n" + form.chatgpt_answer + "\n" + adapt_restrict, role_llm); const roles = await text2videoService.submitLLM("story:\n" + form.chatgpt_answer + "\n" + adapt_restrict, role_llm);
form.all_roles = roles.replace(/。/g, '').replace(/、/g, ',') form.all_roles = roles.replace(/。/g, '').replace(/、/g, ',')
console.log(form.all_roles) console.log(form.all_roles)
const roles_arr = form.all_roles.split(/[,,]/); const roles_arr = form.all_roles.split(/[,,]/);
...@@ -105,7 +105,7 @@ const onAdaptRoles = async () => { ...@@ -105,7 +105,7 @@ const onAdaptRoles = async () => {
Requirement: Requirement:
Keywords are separated by commas. Keywords are separated by commas.
As long as the keyword is returned, no additional explanatory text is required.`; As long as the keyword is returned, no additional explanatory text is required.`;
let keywords = await text2videoService.submitGpt("story:\n" + form.chatgpt_answer + "\n" + adapt_keyword_restrict, role_keywords_llm); let keywords = await text2videoService.submitLLM("story:\n" + form.chatgpt_answer + "\n" + adapt_keyword_restrict, role_keywords_llm);
keywords = keywords.replace(/。/g, '').replace(/、/g, ',') keywords = keywords.replace(/。/g, '').replace(/、/g, ',')
form.chatgpt_answer_roles.push({ form.chatgpt_answer_roles.push({
"角色": one_role.trim(), "角色": one_role.trim(),
...@@ -210,7 +210,7 @@ const onAdaptOne = async (item: any) => { ...@@ -210,7 +210,7 @@ const onAdaptOne = async (item: any) => {
Requirement: Requirement:
Keywords are separated by commas. Keywords are separated by commas.
As long as the keyword is returned, no additional explanatory text is required.`; As long as the keyword is returned, no additional explanatory text is required.`;
const keywords = await text2videoService.submitGpt("story:\n" + form.chatgpt_answer + "\n" + adapt_restrict, tuili_llm); const keywords = await text2videoService.submitLLM("story:\n" + form.chatgpt_answer + "\n" + adapt_restrict, tuili_llm);
// console.log(keywords) // console.log(keywords)
item.场景关键词 = keywords; item.场景关键词 = keywords;
if (form.chatgpt_answer_roles.length === 0) { if (form.chatgpt_answer_roles.length === 0) {
...@@ -222,7 +222,7 @@ const onAdaptOne = async (item: any) => { ...@@ -222,7 +222,7 @@ const onAdaptOne = async (item: any) => {
const adapt_role_restrict = ` const adapt_role_restrict = `
Instructions: Instructions:
Please understand this story and for the scene: "${item.场景描述}", select the character in this scene from characters, with multiple characters separated by commas.`; Please understand this story and for the scene: "${item.场景描述}", select the character in this scene from characters, with multiple characters separated by commas.`;
const item_roles = await text2videoService.submitGpt("story:\n" + form.chatgpt_answer + "\ncharacters:\n"+ form.all_roles +"\n" + adapt_role_restrict, tuili_llm); const item_roles = await text2videoService.submitLLM("story:\n" + form.chatgpt_answer + "\ncharacters:\n"+ form.all_roles +"\n" + adapt_role_restrict, tuili_llm);
// console.log(role_keywords) // console.log(role_keywords)
item.角色 = item_roles; item.角色 = item_roles;
let role_kws = "" let role_kws = ""
...@@ -279,7 +279,7 @@ const onDrawOne = async (item: any) => { ...@@ -279,7 +279,7 @@ const onDrawOne = async (item: any) => {
if (item.场景关键词) {temp_prompt = temp_prompt + `Scene keywords are: ${item.场景关键词}\n`}; if (item.场景关键词) {temp_prompt = temp_prompt + `Scene keywords are: ${item.场景关键词}\n`};
if (item.角色) {temp_prompt = temp_prompt + `Characters in the scene are: ${item.角色}\n`}; if (item.角色) {temp_prompt = temp_prompt + `Characters in the scene are: ${item.角色}\n`};
if (item.角色关键词) {temp_prompt = temp_prompt + `Character keywords are: ${item.角色关键词}\n`}; if (item.角色关键词) {temp_prompt = temp_prompt + `Character keywords are: ${item.角色关键词}\n`};
const sd_describe = await text2videoService.submitGpt( const sd_describe = await text2videoService.submitLLM(
`${temp_prompt} `${temp_prompt}
Instructions: Instructions:
Please understand the above content and return an English description.`, fanyi_llm Please understand the above content and return an English description.`, fanyi_llm
......
<script setup lang="ts">
import { onMounted, reactive, ref } from "vue";
import { Sunny, UploadFilled } from "@element-plus/icons-vue";
import { ElMessage, genFileId,
type UploadInstance,
type UploadProps,
type UploadRawFile } from "element-plus";
import text2videoService from "@/api/service/text2videoService";
import utils from "@/utils/utils";
import { useManyValues } from './compositions/useManyValues'
const debug = ref(import.meta.env.MODE === 'production' ? false : true);
const loading = ref(false);
const dialogVisible = ref(false);
const dialogData = ref("");
const default_data = useManyValues();
const form = reactive({
screen: default_data.screen,
if_need_subtitle: default_data.if_need_subtitle,
chatgpt_prompt: "",
chatgpt_answer: "",
chatgpt_answer_roles: <Wm.RolesItem[]>[],
adapt_result_json: <Wm.ScriptsItem[]>[],
task_id: "",
final_video: "",
});
const sd_prompt_prefix = default_data.sd_prompt_prefix;
const sd_negative_prompt_prefix = default_data.sd_negative_prompt_prefix;
const tyqw = {'api': 'tyqw', 'name':'通义千问'};
const baichuan = {'api': 'langchain', 'name':'baichuan2-7b'};
const qwen = {'api': 'langchain', 'name':'Qwen-7B-Chat'};
const wenan_llm = qwen.api
const wenan_llm_name = qwen.name
const role_llm = tyqw.api
const role_llm_name = tyqw.name
const role_keywords_llm = qwen.api
const role_keywords_llm_name = qwen.name
const tuili_llm = qwen.api
const tuili_llm_name = qwen.name
const fanyi_llm = qwen.api
const fanyi_llm_name = qwen.name
onMounted(() => {
// 初始化示例数据
onChangeScreen(form.screen);
});
const delay = (ms: any) => new Promise(res => setTimeout(res, ms));
const onSubmitGpt = () => {
text2videoService
.submitGpt(form.chatgpt_prompt, wenan_llm)
.then((result: string) => {
console.log(form.chatgpt_prompt);
console.log(result);
form.chatgpt_answer = result;
})
.catch((error: any) => {
// console.error(error);
ElMessage({
message: error,
type: "error",
});
});
};
const onAdaptRoles = async () => {
if (!form.chatgpt_answer || form.chatgpt_answer.length == 0) {
ElMessage({
message: "文案不能为空",
type: "error",
});
return;
}
loading.value = true;
// 推理角色
form.chatgpt_answer_roles = [];
try {
const adapt_restrict = `
Instructions:
Please understand this story and provide all the characters in it, with multiple characters separated by commas`;
let roles = await text2videoService.submitGpt("story:\n" + form.chatgpt_answer + "\n" + adapt_restrict, role_llm);
roles = roles.replace(/。/g, '').replace(/、/g, ',')
console.log(roles)
const roles_arr = roles.split(/[,,]/);
console.log(roles_arr)
async function processRoles() {
for (const one_role of roles_arr) {
await delay(100);
const adapt_keyword_restrict = `
Instructions:
Please understand this story and provide the keywords for the character "${one_role.trim()}" (gender (can be supplemented with imagination, but must have it), age (can be supplemented with imagination, but must have it),
Skin color (can be supplemented with imagination, but must have it), clothing (can be supplemented with imagination, but must have it), hairstyle (can be supplemented with imagination, but must have it),
Hair color (can be supplemented with imagination, but must have it), facial color (can be supplemented with imagination, but must have it), facial features (can be supplemented with imagination, but must have it).
Requirement:
Keywords are separated by commas.
As long as the keyword is returned, no additional explanatory text is required.`;
let keywords = await text2videoService.submitGpt(form.chatgpt_answer + "\n" + adapt_keyword_restrict, role_keywords_llm);
keywords = keywords.replace(/。/g, '').replace(/、/g, ',')
form.chatgpt_answer_roles.push({
"角色": one_role.trim(),
"角色关键词": keywords.trim()+",dressed"
});
}
}
try {
await processRoles();
console.log(form.chatgpt_answer_roles)
} catch (error) {
ElMessage({
message: String(error),
type: "error"
});
} finally {
loading.value = false; // 最终关闭loading(无论成功或失败)
}
} catch (error) {
ElMessage({
message: String(error),
type: "error",
});
} finally {
// 最终关闭loading(无论成功或失败)
loading.value = false;
}
};
const onAdapt = async () => {
if (!form.chatgpt_answer || form.chatgpt_answer.length == 0) {
ElMessage({
message: "文案不能为空",
type: "error",
});
return;
}
loading.value = true;
form.task_id = utils.genDateTimeStr();
console.log(form.task_id)
// 按标点拆分成分镜
const sentences = utils.splitTextEn(form.chatgpt_answer);
console.log(sentences.length)
// 分镜
form.adapt_result_json = []
for (let i = 0; i < sentences.length; i++) {
form.adapt_result_json.push({
"编号": (i + 1).toString(),
"场景描述": sentences[i].trim(),
"场景关键词": "",
"角色": "",
"角色关键词": "",
"画面描述词": "",
"本镜配图": "",
"local_image_path": "",
});
}
console.log(form.adapt_result_json)
async function processScenes() {
for (const item of form.adapt_result_json) {
await onAdaptOne(item);
await delay(100);
await onDrawOne(item);
}
}
try {
await processScenes();
ElMessage({
message: "all scene ok",
type: "success"
});
console.log(form.adapt_result_json);
} catch (error) {
ElMessage({
message: String(error),
type: "error"
});
} finally {
loading.value = false; // 最终关闭loading(无论成功或失败)
}
};
const onAdaptOne = async (item: any) => {
if (!item.场景描述) {
ElMessage({
message: "分镜场景描述不能为空",
type: "error",
});
return;
}
// 推理关键词
try {
const adapt_restrict = `
Instructions:
Please understand this story and provide the keywords for the scene "${item.场景描述}" (era (can be supplemented with imagination, but must be present), space (can be supplemented with imagination, but must be present),
Time period (imagination can be used to supplement, but it must be present), geographical environment (imagination can be used to supplement, but it must be present), weather (imagination can be used to supplement, but it must be present),
Items (can be supplemented with imagination, but must be present), characters (can be supplemented with imagination, but must be present), camera angles (can be supplemented with imagination, but must be present).
Requirement:
Keywords are separated by commas.
As long as the keyword is returned, no additional explanatory text is required.
`;
const keywords = await text2videoService.submitGpt("story:\n" + form.chatgpt_answer + "\nall characters:\n"+ JSON.stringify(form.chatgpt_answer_roles)+"\n" + adapt_restrict);
// console.log(keywords)
const keywords_obj = utils.formatJsonObj(keywords.replace(/```json/g, '').replace(/```/g, ''))
// console.log(keywords_obj)
item.场景关键词 = JSON.stringify(keywords_obj[0]["Scene Keywords"]).replace(/"/g, '');
if (form.chatgpt_answer_roles.length === 0) {
// 总角色为空
item.角色 = '';
item.角色关键词 = '';
} else {
// 总角色不为空
item.角色 = JSON.stringify(keywords_obj[0]["Role"]).replace(/"/g, '');
item.角色关键词 = JSON.stringify(keywords_obj[0]["Role Keywords"]).replace(/"/g, '');
}
} catch (error) {
ElMessage({
message: String(error),
type: "error",
});
}
};
const onDrawOne = async (item: any) => {
if (!item.场景描述 && !item.场景关键词) {
ElMessage({
message: "场景描述和场景关键词不能都为空",
type: "error",
});
return;
}
// 翻译+画图
if (!form.task_id) {
form.task_id = utils.genDateTimeStr();
console.log(form.task_id)
}
try {
let temp_prompt = ""
if (item.场景描述) {temp_prompt = temp_prompt + `Scene description is: ${item.场景描述}\n`};
if (item.场景关键词) {temp_prompt = temp_prompt + `Scene keywords are: ${item.场景关键词}\n`};
if (item.角色) {temp_prompt = temp_prompt + `Characters in the scene are: ${item.角色}\n`};
if (item.角色关键词) {temp_prompt = temp_prompt + `Character keywords are: ${item.角色关键词}\n`};
const sd_describe = await text2videoService.submitGpt(
`${temp_prompt}
Instructions:
Please understand the above content and return an English description.`, fanyi_llm
);
item.画面描述词 = sd_describe;
const sd_prompt = item.画面描述词 + "," + sd_prompt_prefix;
let width = "960";
let height = "540";
if (form.screen == "竖屏") {
width = "540";
height = "960";
}
// console.log(sd_prompt);
// console.log(sd_negative_prompt_prefix);
const sampler_index = "DPM++ SDE Karras";
const seed = "-1";
const steps = "6";
const cfg_scale = "2";
const sd_img = await text2videoService.submitSD(form.task_id, item.编号, sd_prompt, sd_negative_prompt_prefix, width, height, sampler_index, seed, steps, cfg_scale);
item.本镜配图 = sd_img.domain_image_path+"?v="+utils.genDateTimeStr();
item.local_image_path = sd_img.local_image_path;
} catch (error) {
ElMessage({
message: String(error),
type: "error",
});
}
};
const onGenVideo = () => {
if (!form.adapt_result_json || form.adapt_result_json.length == 0 ) {
ElMessage({
message: "必要信息不能为空,请重新执行",
type: "error",
});
return;
}
let is_all_ok = true;
form.adapt_result_json.map(item => {
if (item.编号 == "" || item.场景描述 == "" || item.local_image_path == "") {
ElMessage({
message: `分镜 ${item.编号} 的必要信息为空,请重新执行`,
type: "error",
});
is_all_ok = false;
}
});
if (!is_all_ok) return;
console.log(form.adapt_result_json);
const video_param_detail = form.adapt_result_json.map(item => {
return {
idx: item.编号,
text: item.场景描述,
img_path: item.local_image_path
};
});
const video_param = {
task_id: form.task_id,
if_need_subtitle: form.if_need_subtitle,
lang: 'en',
task_info: video_param_detail,
}
text2videoService
.submitGenVideo(video_param)
.then((result: string) => {
console.log(result);
form.final_video = "";
form.final_video = result+"?v="+utils.genDateTimeStr();
})
.catch((error: any) => {
// console.error(error);
ElMessage({
message: error,
type: "error",
});
});
};
const clean_demo = () => {
form.chatgpt_prompt = "";
form.chatgpt_answer = "";
form.chatgpt_answer_roles = <Wm.RolesItem[]>[];
form.adapt_result_json = <Wm.ScriptsItem[]>[];
form.task_id = "";
form.final_video = "";
}
const clean_roles = () => {
form.chatgpt_answer_roles = <Wm.RolesItem[]>[];
}
const onChangeScreen = (val: string) => {
if (debug.value == true) {
if (val == "竖屏") {
form.task_id = default_data.en_vertical_data.task_id;
form.chatgpt_prompt = default_data.en_vertical_data.chatgpt_prompt;
form.chatgpt_answer = default_data.en_vertical_data.chatgpt_answer;
form.chatgpt_answer_roles = default_data.en_vertical_data.chatgpt_answer_roles;
form.adapt_result_json = default_data.en_vertical_data.adapt_result_json;
form.final_video = default_data.en_vertical_data.final_video;
}
}
}
const showsdprompt = (item: any) => {
// alert(item.画面描述词)
dialogData.value = item.画面描述词+ "," +sd_prompt_prefix+'===== negative ====='+sd_negative_prompt_prefix;
dialogVisible.value = true; // 打开对话框
}
const upload = ref<UploadInstance>()
const actionUrl = ref(
import.meta.env.MODE === 'production'
? '/file'
: import.meta.env.VITE_APP_BASE_API + '/file'
)
const handleUploadSuccess = (val: Wm.UploadResult) => {
if (val.code == 0){
// console.log(val)
const id = parseInt(val.message) - 1;
form.adapt_result_json[id].本镜配图 = val.data[0].url+"?v="+utils.genDateTimeStr();
form.adapt_result_json[id].local_image_path = val.data[0].path;
ElMessage({
message: '上传成功',
type: 'success'
})
} else {
ElMessage({
message: '上传失败',
type: 'error'
})
}
}
const handleExceed: UploadProps['onExceed'] = (files) => {
upload.value!.clearFiles()
const file = files[0] as UploadRawFile
file.uid = genFileId()
upload.value!.handleStart(file)
upload.value!.submit()
}
</script>
<template>
<main class="home-container">
<!-- 标题 -->
<el-divider content-position="left">text2video</el-divider>
<el-form :model="form" label-width="114px" v-loading="loading">
<el-form-item>
<div>
<el-radio-group v-model="form.screen" @change="onChangeScreen">
<!-- <el-radio label="横屏" size="large" border/> -->
<el-radio label="竖屏" size="large" border/>
</el-radio-group>
</div>
</el-form-item>
<el-form-item>
<el-button type="success" @click="clean_demo">清除所有数据</el-button>
</el-form-item>
<!-- Prompt到文案 -->
<!-- <el-form-item label="Prompt">
<el-input v-model="form.chatgpt_prompt" :autosize="true" type="textarea" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmitGpt">生成文案({{wenan_llm_name}})</el-button>
</el-form-item> -->
<el-form-item label="文案">
<el-input v-model="form.chatgpt_answer" :autosize="true" type="textarea" />
</el-form-item>
<!-- 角色 -->
<el-form-item>
<el-button type="primary" @click="onAdaptRoles">推理角色({{role_llm_name}})、推理角色关键词({{role_keywords_llm_name}})</el-button>
<el-button plain @click="clean_roles">清空总角色列表</el-button>
</el-form-item>
<el-form-item label="角色">
<el-table :data="form.chatgpt_answer_roles" border style="width: 100%; z-index: calc(var(--el-table-index) -1)">
<el-table-column prop="角色" label="角色">
<template v-slot="scope">
<el-input v-model="scope.row.角色" :autosize="true" type="textarea"></el-input>
</template>
</el-table-column>
<el-table-column prop="角色关键词" label="角色关键词">
<template v-slot="scope">
<el-input v-model="scope.row.角色关键词" :autosize="true" type="textarea"></el-input>
</template>
</el-table-column>
</el-table>
</el-form-item>
<!-- 分镜 -->
<el-form-item>
<el-button type="primary" @click="onAdapt">分镜、推理场景关键词({{tuili_llm_name}})、英文描述({{fanyi_llm_name}})、绘图</el-button>
</el-form-item>
<el-form-item label="分镜">
<el-table :data="form.adapt_result_json" border style="width: 100%; z-index: calc(var(--el-table-index) -1)">
<el-table-column prop="编号" label="编号" width="60" />
<el-table-column prop="场景描述" label="场景描述">
<template v-slot="scope">
<el-input v-model="scope.row.场景描述" :autosize="true" type="textarea"></el-input>
</template>
</el-table-column>
<el-table-column prop="场景关键词" label="场景关键词">
<template v-slot="scope">
<el-input v-model="scope.row.场景关键词" :autosize="true" type="textarea"></el-input>
</template>
</el-table-column>
<el-table-column prop="角色" label="角色">
<template v-slot="scope">
<el-input v-model="scope.row.角色" :autosize="true" type="textarea"></el-input>
</template>
</el-table-column>
<el-table-column prop="角色关键词" label="角色关键词">
<template v-slot="scope">
<el-input v-model="scope.row.角色关键词" :autosize="true" type="textarea"></el-input>
</template>
</el-table-column>
<el-table-column prop="本镜配图" label="本镜配图" width="300">
<template v-slot="scope">
<div>
<el-image :src="scope.row.本镜配图" :zoom-rate="1.2" :max-scale="1.5" :min-scale="0.5"
:preview-src-list="[scope.row.本镜配图]" fit="cover" :hide-on-click-modal="true"
/>
</div>
</template>
</el-table-column>
<el-table-column width="120" label="操作" align="center">
<!--
<template v-slot:header>
<el-button type="danger" size="default" @click="">批量绘制所有图片</el-button>
</template>
-->
<template v-slot="scope">
<div style="margin: 10px 0"><el-button type="primary" size="default" @click="onAdaptOne(scope.row)">推理关键词</el-button></div>
<div style="margin: 10px 0"><el-button type="primary" size="default" @click="onDrawOne(scope.row)">翻译、绘图</el-button></div>
<el-upload
class="upload-demo"
ref="upload"
list-type="picture"
:show-file-list="false"
:limit="1"
:action="actionUrl"
:on-success="handleUploadSuccess"
:on-exceed="handleExceed"
:data="{item_id: scope.row.编号}"
>
<el-button type="primary">上传图片</el-button>
</el-upload>
<div style="margin: 10px 0"><el-button plain @click="showsdprompt(scope.row)">debug</el-button></div>
<el-dialog
v-model=dialogVisible
width="80%"
>
<p>{{ dialogData }}</p>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="dialogVisible = false">ok</el-button>
</div>
</template>
</el-dialog>
</template>
</el-table-column>
</el-table>
</el-form-item>
<!-- 生成视频 -->
<el-form-item>
<el-button type="primary" @click="onGenVideo">生成视频</el-button>
</el-form-item>
<el-form-item>
<video :src="form.final_video" controls></video>
</el-form-item>
</el-form>
</main>
</template>
<style lang="scss" scoped>
.home-container {
width: 100%;
}
</style>
<style lang="scss">
.home-container {
.el-table .el-table__cell {
z-index: calc(var(--el-table-index) -1);
}
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment