1. 文案处理后端服务迁移(此版本之前的后端服务持续到12月1日)
2. 新增默认动图方式
3. (聚合推文)实现原创分类的图生视频(runway,luma,可灵)
4. 修复软件内配音
5. 删除旧的原创生图(原创,SD反推,MJ反推等操作在聚合推文中)
6. 导出剪映适配(视频)
7. 新增四个单句推理模式
This commit is contained in:
2024-11-19 20:28:31 +08:00
parent 6fa58e4d94
commit f17de033e1
61 changed files with 3417 additions and 361 deletions
@@ -1,6 +1,9 @@
<template>
<div style="display: flex; align-items: center">
<span>参数</span>
<div>
<span v-if="reverseManageStore.selectBook.type == 'original'">参数/菜单</span>
<span v-else>参数</span>
</div>
<n-dropdown
trigger="hover"
:options="options"
@@ -19,7 +22,7 @@ import { ref } from 'vue'
import { NDropdown, NButton, NIcon, useMessage } from 'naive-ui'
import MenuOpenRound from '@/renderer/src/components/Icon/MenuOpenRound.vue'
import { useReverseManageStore } from '@/stores/reverseManage'
import { BookImageCategory } from '@/define/enum/bookEnum'
import { BookImageCategory, BookType } from '@/define/enum/bookEnum'
import { useSoftwareStore } from '@/stores/software'
import { TimeDelay } from '@/define/Tools/time'
@@ -33,7 +36,12 @@ let softwareStore = useSoftwareStore()
let options = [
{ label: '启用修脸', key: 'openFaceRepair' },
{ label: '关闭修脸', key: 'closeFaceRepair' }
{ label: '关闭修脸', key: 'closeFaceRepair' },
{
label: () =>
reverseManageStore.selectBookTask.openVideoGenerate ? '关闭合成视频' : '开启合成视频',
key: 'openGenerateVideo'
}
]
async function ModifyFaceRepair(key) {
@@ -75,6 +83,9 @@ async function handleSelect(key) {
case 'closeFaceRepair':
await ModifyFaceRepair(key)
break
case 'openGenerateVideo':
await ModifyGenerateVideo()
break
default:
message.error('未知操作')
break
@@ -86,6 +97,15 @@ async function handleSelect(key) {
}
}
async function ModifyGenerateVideo() {
softwareStore.spin.tip = '正在修改表格数据'
reverseManageStore.selectBookTask.openVideoGenerate =
!reverseManageStore.selectBookTask.openVideoGenerate
await window.db.UpdateBookTaskData(reverseManageStore.selectBookTask.id, {
openVideoGenerate: reverseManageStore.selectBookTask.openVideoGenerate
})
}
function renderOption({ node, option }) {
if (option.key === 'openFaceRepair' || option.key === 'closeFaceRepair') {
if (
@@ -94,6 +114,10 @@ function renderOption({ node, option }) {
) {
return node
}
} else if (option.key === 'openGenerateVideo') {
if (reverseManageStore.selectBook.type == BookType.ORIGINAL) {
return node
}
} else {
return node
}
@@ -0,0 +1,105 @@
<template>
<div class="data-table-generate">
<div class="data-table-generate-progress">
<n-button size="tiny" :color="softwareStore.SoftColor.BROWN_YELLOW" @click="OpenVideoOptions"
>视频</n-button
>
<div>
<n-tag
v-if="bookTaskDetail.videoMessage?.status != 'fail'"
style="margin-left: 5px"
:bordered="false"
type="success"
size="small"
>
{{ GetVideoStatus(bookTaskDetail.videoMessage?.status ?? 'wait') }}
</n-tag>
<div v-else>
<n-popover style="padding: 0; margin: 0" trigger="hover">
<template #trigger>
<n-tag style="margin-left: 5px" :bordered="false" type="error" size="small">
{{ GetVideoStatus(bookTaskDetail.videoMessage?.status ?? 'fail') }}
</n-tag>
</template>
<span>{{ bookTaskDetail.videoMessage?.msg }}</span>
</n-popover>
</div>
</div>
<n-popover style="padding: 0; margin: 0" trigger="hover">
<template #trigger>
<div
style="
margin-left: 5px;
width: 100px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
"
>
{{ bookTaskDetail.videoMessage?.taskId ?? '' }}
</div>
</template>
<span>{{ bookTaskDetail.videoMessage?.taskId ?? '' }}</span>
</n-popover>
</div>
<div>
<div v-if="showVideo" class="data-table-generate-video"></div>
<div v-else style="display: flex; justify-content: center; align-items: center;">
<video style="max-width: 180px; max-height: 120px" controls>
<source :src="bookTaskDetail.generateVideoPath" type="video/mp4" />
Your browser does not support the video tag.
</video>
</div>
</div>
</div>
</template>
<script setup>
import { isEmpty } from 'lodash'
import { computed, onMounted, ref, h } from 'vue'
import { NButton, useDialog, NTag, NPopover } from 'naive-ui'
import { useSoftwareStore } from '@/stores/software'
import GenerateVideoDialog from './GenerateVideoDialog.vue'
import { useReverseManageStore } from '@/stores/reverseManage'
import { GetVideoStatus } from '@/define/enum/video'
let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore()
let dialog = useDialog()
let props = defineProps({
bookTaskDetail: undefined,
index: undefined
})
let bookTaskDetail = ref(props.bookTaskDetail)
let showVideo = computed(() => {
return isEmpty(bookTaskDetail?.value?.generateVideoPath)
})
async function OpenVideoOptions() {
dialog.create({
showIcon: false,
title: '图/文 生视频',
content: () => h(GenerateVideoDialog, { bookTaskDetailId: bookTaskDetail.value.id }),
style: 'width : 800px',
maskClosable: false
})
}
</script>
<style scoped>
.data-table-generate {
height: 145px;
}
.data-table-generate-video {
height: 120px;
}
.data-table-generate-progress {
display: flex;
align-items: center;
}
</style>
@@ -0,0 +1,215 @@
<template>
<div>
<n-form inline :label-width="180">
<n-form-item label="动图服务商" path="videoMessage.videoType" style="width: 200px">
<n-select
:options="videoOptions"
v-model:value="videoMessage.videoType"
placeholder="请选择动图服务商"
></n-select>
</n-form-item>
<n-form-item label="生成方式" path="videoMessage.model" style="width: 200px">
<n-select
:options="videoModelOptions"
v-model:value="videoMessage.model"
placeholder="请选择动图生图方式"
></n-select>
</n-form-item>
<n-form-item>
<n-button type="primary" @click="SaveSimpleOptions">保存</n-button>
</n-form-item>
<n-form-item>
<n-button type="primary" @click="AddImageToVideoTask">添加转视频任务</n-button>
</n-form-item>
</n-form>
<n-divider />
<div>
<RunwayOption
ref="runwayRef"
:options="runwayOptions"
:motionVector="runwayOptions.options?.motion_vector"
v-if="videoMessage.videoType == videoType.RUNWAY"
/>
<LumaOption
ref="lumaRef"
:lumaOptions="lumaOptions"
v-else-if="videoMessage.videoType == videoType.LUMA"
/>
<KlingOption
:klingOptions="klingOptions"
v-else-if="videoMessage.videoType == videoType.KLING"
/>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useMessage, NForm, NFormItem, NDivider, NButton, NSelect } from 'naive-ui'
import { ValidateJson } from '@/define/Tools/validate'
import { ImageToVideoModels, VideoModel, VideoStatus } from '@/define/enum/video'
import { useSoftwareStore } from '@/stores/software'
import RunwayOption from './RunwayOption.vue'
import LumaOption from './LumaOption.vue'
import KlingOption from './KlingOption.vue'
import { BookBackTaskType, TaskExecuteType } from '@/define/enum/bookEnum'
import { useReverseManageStore } from '@/stores/reverseManage'
import { DEFINE_STRING } from '@/define/define_string'
import { option } from 'artplayer'
let props = defineProps({
bookTaskDetailId: undefined
})
let videoType = ref(ImageToVideoModels)
let reverseManageStore = useReverseManageStore()
let message = useMessage()
let softwareStore = useSoftwareStore()
let runwayRef = ref(null)
let videoMessage = ref({})
let runwayOptions = ref({})
let lumaOptions = ref({})
let klingOptions = ref({})
async function GetBookTaskDetailOption() {
debugger
let res = await window.db.GetBookTaskDetailProperty(props.bookTaskDetailId, 'videoMessage')
if (res.code != 1) {
message.error(res.message)
}
if (res.data != null) {
videoMessage.value = res.data
} else {
// 这边初始化
let initRes = await window.book.InitVideoMessage(props.bookTaskDetailId)
console.log('InitVideoMessage', initRes)
if (initRes.code != 1) {
message.error(initRes.message)
return
}
videoMessage.value = initRes.data
}
console.log(videoMessage.value)
if (ValidateJson(videoMessage.value.runwayOptions)) {
runwayOptions.value = JSON.parse(videoMessage.value.runwayOptions)
runwayOptions.value.image = videoMessage.value.imageUrl
}
if (ValidateJson(videoMessage.value.lumaOptions)) {
lumaOptions.value = JSON.parse(videoMessage.value.lumaOptions)
lumaOptions.value.image_url = videoMessage.value.imageUrl
}
if (ValidateJson(videoMessage.value.klingOptions)) {
klingOptions.value = JSON.parse(videoMessage.value.klingOptions)
klingOptions.value.image = videoMessage.value.imageUrl
}
}
onMounted(async () => {
await GetBookTaskDetailOption()
})
async function SaveSimpleOptions() {
try {
// 保存数据
// 这边修改数据然后直接保存
let saveVideoMessageObject = {}
debugger
if (videoMessage.value.videoType == ImageToVideoModels.RUNWAY) {
runwayOptions.value.options = runwayRef.value.options
runwayOptions.value.options.motion_vector = runwayRef.value.motion_vector
saveVideoMessageObject = {
...videoMessage.value,
prompt: runwayRef.value.prompt,
style: runwayRef.value.style,
model: videoMessage.value.model,
bookTaskDetailId: props.bookTaskDetailId,
status: VideoStatus.WAIT,
runwayOptions: JSON.stringify(runwayOptions.value),
lumaOptions: JSON.stringify(lumaOptions.value),
klingOptions: JSON.stringify(klingOptions.value)
}
saveVideoMessageObject.imageUrl = runwayOptions.value.image
} else if (videoMessage.value.videoType == ImageToVideoModels.LUMA) {
saveVideoMessageObject = {
...videoMessage.value,
model: videoMessage.value.model,
bookTaskDetailId: props.bookTaskDetailId,
status: VideoStatus.WAIT,
runwayOptions: JSON.stringify(runwayOptions.value),
lumaOptions: JSON.stringify(lumaOptions.value),
klingOptions: JSON.stringify(klingOptions.value)
}
saveVideoMessageObject.imageUrl = lumaOptions.value.image_url
} else if (videoMessage.value.videoType == ImageToVideoModels.KLING) {
saveVideoMessageObject = {
...videoMessage.value,
model: videoMessage.value.model,
bookTaskDetailId: props.bookTaskDetailId,
status: VideoStatus.WAIT,
runwayOptions: JSON.stringify(runwayOptions.value),
lumaOptions: JSON.stringify(lumaOptions.value),
klingOptions: JSON.stringify(klingOptions.value)
}
saveVideoMessageObject.imageUrl = klingOptions.value.image
} else {
message.error('保存失败,未知的转视频类型')
return
}
console.log('saveVideoMessageObject', saveVideoMessageObject)
let res = await window.book.UpdateBookTaskDetailVideoMessage(
props.bookTaskDetailId,
saveVideoMessageObject
)
if (res.code == 0) {
message.error(res.message)
return
}
message.success('保存成功')
} catch (error) {
message.error('保存失败,' + error.message)
throw new Error('保存失败,' + error.message)
}
}
async function AddImageToVideoTask() {
try {
// 先保存数据
await SaveSimpleOptions()
let type = BookBackTaskType.RUNWAY_VIDEO
if (videoMessage.value.videoType == ImageToVideoModels.LUMA) {
type = BookBackTaskType.LUMA_VIDEO
} else if (videoMessage.value.videoType == ImageToVideoModels.KLING) {
type = BookBackTaskType.KLING_VIDEO
}
debugger
// 添加任务
let res = await window.task.AddBookBackTask(
reverseManageStore.selectBook.id,
type,
TaskExecuteType.AUTO,
reverseManageStore.selectBookTask.id,
props.bookTaskDetailId,
DEFINE_STRING.BOOK.RUNWAY_IMAGE_TO_VIDEO_RETURN
)
if (res.code != 1) {
message.error(res.message)
return
}
message.success('添加图转视频任务到队列成功')
} catch (error) {
message.error('添加任务到队列失败,错误消息如下:' + error.message)
}
}
let videoOptions = ref([
{ label: 'Runway', value: ImageToVideoModels.RUNWAY },
{ label: 'Luma', value: ImageToVideoModels.LUMA },
{ label: '可灵', value: ImageToVideoModels.KLING }
])
let videoModelOptions = ref([
{ label: '文生视频', value: VideoModel.TEXT_TO_VIDEO },
{ label: '图生视频', value: VideoModel.IMAGE_TO_VIDEO }
])
</script>
@@ -0,0 +1,99 @@
<template>
<div>
<n-form :model="klingOptions" label-placement="top">
<n-grid :cols="24" :x-gap="24">
<n-form-item-gi :span="12" label="提示词">
<n-input v-model:value="klingOptions.prompt" placeholder="请输入提示词"></n-input>
</n-form-item-gi>
<n-form-item-gi :span="12" label="反向提示词">
<n-input
v-model:value="klingOptions.negative_prompt"
placeholder="请输反向提示词"
></n-input>
</n-form-item-gi>
<n-form-item-gi :span="6" label="生成视频的自由度">
<n-input-number
v-model:value="klingOptions.cfg_scale"
placeholder="请输入生成视频的自由度"
></n-input-number>
</n-form-item-gi>
<n-form-item-gi :span="6" label="生成视频的模式">
<n-select
v-model:value="klingOptions.mode"
:options="KlingModeOptions"
placeholder="请选择生成视频的模式"
>
</n-select>
</n-form-item-gi>
<n-form-item-gi :span="6" label="生成视频时长">
<n-select
v-model:value="klingOptions.seconds"
:options="KlingSecondsOptions"
placeholder="请选择生成视频时长"
>
</n-select>
</n-form-item-gi>
<n-form-item-gi :span="12" label="参考图片链接">
<n-input v-model:value="klingOptions.image" placeholder="请输入参考图片链接"></n-input>
</n-form-item-gi>
<n-form-item-gi :span="12">
<n-image width="100" :src="klingOptions.image"></n-image>
</n-form-item-gi>
<n-form-item-gi :span="12" label="参考图像 - 尾帧控制">
<n-input
v-model:value="klingOptions.image_tail"
placeholder="请输入参考图像 - 尾帧控制"
></n-input>
</n-form-item-gi>
<n-form-item-gi :span="12">
<n-image width="100" :src="klingOptions.image_tail"></n-image>
</n-form-item-gi>
</n-grid>
</n-form>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import {
NForm,
NImage,
NGrid,
NFormItemGi,
NInput,
NCheckbox,
NInputNumber,
NSelect
} from 'naive-ui'
import { KlingMode, RunwaySeconds } from '@/define/enum/video'
let props = defineProps({
klingOptions: undefined
})
let klingOptions = ref({
prompt: '',
negative_prompt: '',
mode: KlingMode.PRO,
seconds: RunwaySeconds.FIVE
})
onMounted(() => {
if (props.klingOptions) {
klingOptions.value = props.klingOptions
}
})
let KlingModeOptions = ref([
{ label: '高表现', value: KlingMode.PRO },
{ label: '高性能', value: KlingMode.STD }
])
let KlingSecondsOptions = ref([
{ label: '5s', value: RunwaySeconds.FIVE },
{ label: '10s', value: RunwaySeconds.TEN }
])
</script>
@@ -0,0 +1,55 @@
<template>
<div>
<n-form :model="lumaOptions" label-placement="top">
<n-grid :cols="24" :x-gap="24">
<n-form-item-gi :span="12" label="提示词">
<n-input v-model:value="lumaOptions.user_prompt" placeholder="请输入提示词"></n-input>
</n-form-item-gi>
<n-form-item-gi :span="4" label="视频比例">
<n-input v-model:value="lumaOptions.aspect_ratio" placeholder="请输视频比例"></n-input>
</n-form-item-gi>
<n-form-item-gi :span="4" label="提示词优化">
<n-checkbox v-model:checked="lumaOptions.expand_prompt">提示词优化</n-checkbox>
</n-form-item-gi>
<n-form-item-gi :span="4" label="循环参考图">
<n-checkbox v-model:checked="lumaOptions.loop">循环参考图</n-checkbox>
</n-form-item-gi>
<n-form-item-gi :span="12" label="参考图片链接">
<n-input v-model:value="lumaOptions.image_url" placeholder="请输入参考图片链接"></n-input>
</n-form-item-gi>
<n-form-item-gi :span="12">
<n-image width="100" :src="lumaOptions.image_url"></n-image>
</n-form-item-gi>
<n-form-item-gi :span="12" label="关键帧图片链接">
<n-input
v-model:value="lumaOptions.image_end_url"
placeholder="请输入关键帧图片链接"
></n-input>
</n-form-item-gi>
<n-form-item-gi :span="12">
<n-image width="100" :src="lumaOptions.image_end_url"></n-image>
</n-form-item-gi>
</n-grid>
</n-form>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { NForm, NImage, NGrid, NFormItemGi, NInput, NCheckbox } from 'naive-ui'
let props = defineProps({
lumaOptions: undefined
})
let lumaOptions = ref({
user_prompt: '123',
aspect_ratio: '4:3',
loop: false
})
onMounted(() => {
if (props.lumaOptions) {
lumaOptions.value = props.lumaOptions
}
})
</script>
@@ -0,0 +1,173 @@
<template>
<div>
<n-form ref="formRef" :model="runwayOptions" label-placement="top">
<n-grid :cols="24" :x-gap="24">
<n-form-item-gi :span="12" label="图片链接" path="image">
<n-input v-model:value="runwayOptions.image" placeholder="请输入图片链接" />
</n-form-item-gi>
<n-form-item-gi :span="12" path="textareaValue">
<div style="display: flex">
<n-image width="100" :src="runwayOptions.image" />
<div style="margin-left: 5px">123</div>
</div>
</n-form-item-gi>
<n-form-item-gi :span="12" label="提示词(支持中文)" path="prompt">
<n-input v-model:value="runwayOptions.prompt" placeholder="请输入提示词(支持中文)" />
</n-form-item-gi>
<n-form-item-gi :span="6" label="runway模型" path="model">
<n-select v-model:value="runwayOptions.model" :options="runwwayModelOptions"></n-select>
</n-form-item-gi>
<n-form-item-gi :span="6" label="风格" path="style">
<n-input v-model:value="runwayOptions.style" placeholder="请输入风格" />
</n-form-item-gi>
<n-form-item-gi :span="6" label="时长(秒)" path="options">
<n-select v-model:value="options.seconds" :options="runwwaySecondOptions"></n-select>
</n-form-item-gi>
<n-form-item-gi :span="6" path="last_image" label="尾帧">
<n-input v-model:value="runwayOptions.last_image" placeholder="请输入尾帧图片链接" />
</n-form-item-gi>
<n-form-item-gi :span="6" path="options.image_as_end_frame">
<n-checkbox v-model:checked="options.image_as_end_frame"> 是否将图片作为尾帧 </n-checkbox>
</n-form-item-gi>
<n-form-item-gi :span="6" path="options.flip">
<n-checkbox v-model:checked="options.flip"> 是否宽屏169 </n-checkbox>
</n-form-item-gi>
</n-grid>
</n-form>
<n-collapse>
<n-collapse-item title="镜头控制" name="1">
<n-form :model="motion_vector" label-placement="top">
<n-grid :cols="24" :x-gap="24">
<n-gi :span="24" style="margin-bottom: 20px">
<n-checkbox v-model:checked="motion_vector.isUse"> 是否启用镜头控制 </n-checkbox>
</n-gi>
<n-form-item-gi :span="8" label="镜头控制-水平" path="x">
<n-input-number
:show-button="false"
v-model:value="motion_vector.x"
placeholder="请输入水平值"
/>
</n-form-item-gi>
<n-form-item-gi :span="8" label="镜头控制-垂直" path="y">
<n-input-number
:show-button="false"
v-model:value="motion_vector.y"
placeholder="请输入垂直值"
/>
</n-form-item-gi>
<n-form-item-gi :span="8" label="镜头控制-缩放" path="z">
<n-input-number
:show-button="false"
v-model:value="motion_vector.z"
placeholder="请输入缩放值"
/>
</n-form-item-gi>
<n-form-item-gi :span="8" label="镜头控制-旋转" path="r">
<n-input-number
:show-button="false"
v-model:value="motion_vector.r"
placeholder="请输入旋转值"
/>
</n-form-item-gi>
<n-form-item-gi :span="8" label="镜头控制-左右方向倾斜" path="bg_x_pan">
<n-input-number
:show-button="false"
v-model:value="motion_vector.bg_x_pan"
placeholder="请输入左右方向倾斜值"
/>
</n-form-item-gi>
<n-form-item-gi :span="8" label="镜头控制-上下方向倾斜" path="bg_y_pan">
<n-input-number
:show-button="false"
v-model:value="motion_vector.bg_y_pan"
placeholder="请输入上下方向倾斜值"
/>
</n-form-item-gi>
</n-grid>
</n-form>
</n-collapse-item>
</n-collapse>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import {
NForm,
NCheckbox,
NInputNumber,
NCollapse,
NGi,
NCollapseItem,
NInput,
NGrid,
NButton,
NSelect,
NFormItemGi,
NImage
} from 'naive-ui'
import { useSoftwareStore } from '@/stores/software'
import { RunawayModel, RunwaySeconds } from '@/define/enum/video'
let softwareStore = useSoftwareStore()
let props = defineProps({
motionVector: undefined,
options: undefined
})
let runwayOptions = ref(props.options)
let motion_vector = ref({
isUse: false,
x: 0,
y: 0,
z: 0,
r: 0,
bg_x_pan: 0,
bg_y_pan: 0
})
let options = ref({
seconds: RunwaySeconds.FIVE,
image_as_end_frame: false,
flip: false
})
onMounted(() => {
if (props.motionVector) {
motion_vector.value = props.motionVector
}
if (props.options && props.options.options) {
options.value = props.options.options
}
})
let runwwayModelOptions = ref([
{
label: 'Gen 2',
value: RunawayModel.GNE2
},
{
label: 'Gen 3',
value: RunawayModel.GNE3
}
])
let runwwaySecondOptions = ref([
{
label: '5秒',
value: RunwaySeconds.FIVE
},
{
label: '10秒',
value: RunwaySeconds.TEN
}
])
defineExpose({
motion_vector,
options
})
</script>
@@ -83,9 +83,9 @@ const columns = [
key: 'gptPrompt',
className: 'empty-margin',
resizable: true,
width: 400,
minWidth: 400,
maxWidth: 600,
width: 350,
minWidth: 300,
maxWidth: 500,
render(row, index) {
if (reverseManageStore.selectBook.type == BookType.MJ_REVERSE) {
return h(MJReversePrompt, {
@@ -152,7 +152,6 @@ onMounted(async () => {
div[i].style.padding = '0px 5px'
}
calcHeight()
// getMaxHeight()
}, 100)
// 数据加载完毕
@@ -6,7 +6,7 @@
classMame="cd-22223"
:max-height="maxHeight"
:loading="softwareStore.loading.originDatatableDataLoading"
:columns="createColumns({})"
:columns="columns"
:data="reverseManageStore.selectBookTaskDetail"
:pagination="false"
:bordered="false"
@@ -16,7 +16,7 @@
</template>
<script setup>
import { ref, h, onMounted, watch } from 'vue'
import { ref, h, onMounted, watch, computed } from 'vue'
import { NDataTable, useMessage } from 'naive-ui'
import DatatableHeaderAfterGpt from '../MJReverse/DatatableHeaderAfterGpt.vue'
import DatatableAfterGpt from '../MJReverse/DatatableAfterGpt.vue'
@@ -27,12 +27,12 @@ import ODataTableHeaderGptPrompt from './ODataTableHeaderGptPrompt.vue'
import ODataTableHeaderPrompt from './ODatatableHeaderPrompt.vue'
import ODatatablePrompt from './ODatatablePrompt.vue'
import ODataTableAction from './ODataTableAction.vue'
import DatatableHeaderGenerateImage from '../MJReverse/DatatableHeaderGenerateImage.vue'
import DatatableHeaderImage from '../MJReverse/DatatableHeaderImage.vue'
import DatatableGenerateImage from '../MJReverse/DatatableGenerateImage.vue'
import BookTaskDetailOptions from '../Components/ManageBook/BookTaskDetailOptions.vue'
import { useReverseManageStore } from '../../../../../stores/reverseManage'
import { useSoftwareStore } from '../../../../../stores/software'
import DataTableGenerateVideo from '../Components/Video/DataTableGenerateVideo.vue'
let reverseManageStore = useReverseManageStore()
let softwareStore = useSoftwareStore()
let maxHeight = ref(0)
@@ -50,13 +50,8 @@ watch(
)
onMounted(async () => {
setTimeout(() => {
let div = document.getElementsByClassName('empty-margin')
for (let i = 0; i < div.length; i++) {
div[i].style.padding = '0px 5px'
}
getMaxHeight()
}, 100)
getMaxHeight()
// 加载初始数据
// 这边开始加载数据(就是小说分镜任务)
softwareStore.spin.spinning = false
@@ -77,10 +72,16 @@ onMounted(async () => {
async function getMaxHeight() {
let height = window.innerHeight
maxHeight.value = height - 100
setTimeout(() => {
let div = document.getElementsByClassName('empty-margin')
for (let i = 0; i < div.length; i++) {
div[i].style.padding = '0px 5px'
}
}, 10)
}
const createColumns = ({}) => {
return [
const columns = computed(() => {
let cols = [
{
title: 'No.',
key: 'no',
@@ -120,8 +121,8 @@ const createColumns = ({}) => {
key: 'gpt_prompt',
className: 'empty-margin',
resizable: true,
minWidth: 330,
maxWidth: 700,
minWidth: 350,
maxWidth: 500,
width: '360',
render(row, index) {
return h(ODataTableGptPrompt, { initData: row, index: index })
@@ -150,6 +151,17 @@ const createColumns = ({}) => {
return h(ODataTableAction, { initData: row, index: index })
}
},
{
// 参数
title: '合成视频',
key: 'generateVideoPath',
className: 'empty-margin',
width: '200',
minWidth: reverseManageStore.selectBookTask.openVideoGenerate ? 130 : 0,
render(row, index) {
return h(DataTableGenerateVideo, { bookTaskDetail: row, index: index })
}
},
{
// 出图
title(row) {
@@ -167,5 +179,7 @@ const createColumns = ({}) => {
}
}
]
}
getMaxHeight()
return cols.filter((i) => i.minWidth != 0)
})
</script>
@@ -15,6 +15,8 @@ import { useReverseManageStore } from '../../../../../stores/reverseManage'
import OriginalMainButton from './OriginalMainButton.vue'
import OriginalMainDatatale from './OriginalMainDatatable.vue'
import { DEFINE_STRING } from '../../../../../define/define_string'
import { ResponseMessageType } from '@/define/enum/softwareEnum'
import { GetVideoStatus, VideoStatus } from '@/define/enum/video'
let softwareStore = useSoftwareStore()
let reverseManageStore = useReverseManageStore()
let message = useMessage()
@@ -93,6 +95,74 @@ function OriginalSDImageResponseReturn(value) {
}
}
function VideoImageToVideoReturn(value) {
debugger
console.log('VideoImageToVideoReturn', value)
if (value.code == 1) {
// 成功
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex((item) => item.id == value.id)
if (findIndex == -1) {
return
}
if (value.type == ResponseMessageType.RUNWAY_VIDEO) {
let resultData = JSON.parse(value.data)
let status = 0
if (resultData.status == '0' || resultData.status == '') {
status = VideoStatus.WAIT
} else if (resultData.status == '1') {
status = VideoStatus.PROCESSING
} else if (resultData.status == '2') {
status = VideoStatus.FAIL
} else if (resultData.status == '3') {
status = VideoStatus.SUCCESS
}
reverseManageStore.selectBookTaskDetail[findIndex].videoMessage.taskId = resultData.task_id
reverseManageStore.selectBookTaskDetail[findIndex].videoMessage.status = status
reverseManageStore.selectBookTaskDetail[findIndex].videoMessage.messageData = value.data
} else if (value.type == ResponseMessageType.LUMA_VIDEO) {
let resultData = JSON.parse(value.data)
let status = VideoStatus.WAIT
if (resultData.state == 'pending') {
status = VideoStatus.WAIT
} else if (resultData.state == 'processing') {
status = VideoStatus.PROCESSING
} else if (resultData.state == 'completed') {
status = VideoStatus.SUCCESS
} else {
status = VideoStatus.FAIL
}
reverseManageStore.selectBookTaskDetail[findIndex].videoMessage.taskId = resultData.id
reverseManageStore.selectBookTaskDetail[findIndex].videoMessage.status = status
reverseManageStore.selectBookTaskDetail[findIndex].videoMessage.messageData = value.data
} else if (value.type == ResponseMessageType.KLING_VIDEO) {
let resultData = JSON.parse(value.data)
let status = VideoStatus.WAIT
if (resultData.task_status == 'submitted') {
status = VideoStatus.WAIT
} else if (resultData.task_status == 'processing') {
status = VideoStatus.PROCESSING
} else if (resultData.task_status == 'succeed') {
status = VideoStatus.SUCCESS
} else {
status = VideoStatus.FAIL
}
reverseManageStore.selectBookTaskDetail[findIndex].videoMessage.taskId = resultData.task_id
reverseManageStore.selectBookTaskDetail[findIndex].videoMessage.status = status
reverseManageStore.selectBookTaskDetail[findIndex].videoMessage.messageData = value.data
} else if (value.type == ResponseMessageType.VIDEO_SUCESS) {
reverseManageStore.selectBookTaskDetail[findIndex].videoMessage.status = VideoStatus.SUCCESS
reverseManageStore.selectBookTaskDetail[findIndex].generateVideoPath = value.data
}
} else {
let findIndex = reverseManageStore.selectBookTaskDetail.findIndex((item) => item.id == value.id)
if (findIndex == -1) {
return
}
reverseManageStore.selectBookTaskDetail[findIndex].videoMessage.status = VideoStatus.FAIL
reverseManageStore.selectBookTaskDetail[findIndex].videoMessage.msg = value.message
}
}
onMounted(async () => {
// 监听推理提示词任务返回
window.api.setEventListen([DEFINE_STRING.BOOK.ORIGINAL_GPT_PROMPT_RETURN], (value) =>
@@ -122,6 +192,10 @@ onMounted(async () => {
window.api.setEventListen([DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN], (value) => {
OriginalSDImageResponseReturn(value)
})
window.api.setEventListen([DEFINE_STRING.BOOK.RUNWAY_IMAGE_TO_VIDEO_RETURN], (value) => {
VideoImageToVideoReturn(value)
})
})
onUnmounted(() => {
window.api.removeEventListen(DEFINE_STRING.BOOK.ORIGINAL_GPT_PROMPT_RETURN)
@@ -131,5 +205,6 @@ onUnmounted(() => {
window.api.removeEventListen(DEFINE_STRING.BOOK.FLUX_FORGE_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.FLUX_API_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.D3_IMAGE_GENERATE_RETURN)
window.api.removeEventListen(DEFINE_STRING.BOOK.RUNWAY_IMAGE_TO_VIDEO_RETURN)
})
</script>
@@ -27,16 +27,12 @@
class="url_class"
>软件激活教程 -->
</strong>
<strong
style="margin-left: 10px; color: #ea5514"
@click="OpenTeach('lms')"
class="url_class"
>
<strong style="margin-left: 10px; color: #ea5514" @click="OpenTeach('lms')" class="url_class">
后台入口
</strong>
</div>
<!-- <div style="margin: 10px; color: red">
如未激活将上面的码给管理员激活
<div style="margin: 10px; color: red">
有使用问题或者购买问题请联系客服微信
<n-popover trigger="hover" raw :show-arrow="false">
<template #trigger>
<n-tag type="info"> 客服微信 </n-tag>
@@ -47,7 +43,7 @@
preview-disabled
/>
</n-popover>
</div> -->
</div>
<div style="margin: 10px; font-size: large; color: red">
教程视频<span class="url_class" @click="OpenTeach('video')">向北-LAITool</span>
</div>
@@ -87,7 +83,7 @@ function OpenTeach(type) {
'https://rvgyir5wk1c.feishu.cn/docx/RNijdu3w4o4tRlxBhdccWhRpnfg?from=from_copylink'
)
break
case "lms" :
case 'lms':
window.api.OpenUrl('https://lms.laitool.cn')
break
default:
+286 -260
View File
@@ -34,7 +34,7 @@
</template>
<script setup>
import { ref, h, onMounted, defineComponent, toRaw } from 'vue'
import { ref, h, onMounted, defineComponent, toRaw, computed } from 'vue'
import { RouterLink } from 'vue-router'
import {
useDialog,
@@ -78,6 +78,7 @@ let show = ref(true)
const systemStore = useSystemStore()
let key_down_ref = ref(null)
let showMenu = ref(true)
// 菜单列表图标渲染
function renderMenuIcon(option) {
@@ -322,6 +323,9 @@ onMounted(async () => {
window.api.getSettingDafultData(async (value) => {
window.config = value
if (!(window.config && window.config.showOriginal)) {
showMenu.value = false
}
})
})
@@ -329,274 +333,296 @@ function expandIcon(value) {
return h(NIcon, null, { default: () => h(CaretDownOutline) })
}
const menuOptions = [
{
label: () =>
h(
RouterLink,
{
to: {
name: 'gptCopywriting'
const menuOptions = computed(() => {
let me = [
{
label: () =>
h(
RouterLink,
{
to: {
name: 'gptCopywriting'
},
class: 'router-link-a'
},
class: 'router-link-a'
},
{
default: () => '文案处理'
}
),
key: 'gptCopywriting'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'sdoriginal'
{
default: () => '文案处理'
}
},
{
default: () => '原创生图'
}
),
key: 'sdoriginal'
},
{
label: '超级矩阵',
key: 'backward_matrix',
children: [
{
label: () =>
h(
RouterLink,
{
to: {
name: 'getframe'
}
},
{ default: () => '抽帧' }
),
key: 'backward_frame'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'copywriting'
}
},
{ default: () => '文案洗稿' }
),
key: 'copywriting'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'pushBackPrompt'
}
},
{ default: () => '反推提示词' }
),
key: 'push_back'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'regenerate'
}
},
{ default: () => '批次生成' }
),
key: 'regenerate'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'VideoGenerate'
}
},
{ default: () => '重绘/视频合成' }
),
key: 'VideoGenerate'
}
]
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'book_management'
),
key: 'gptCopywriting'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'sdoriginal'
}
},
{
default: () => '原创生图(弃用)'
}
},
),
key: 'sdoriginal',
showMenu: showMenu.value
},
{
label: '超级矩阵',
key: 'backward_matrix',
children: [
{
default: () => '聚合推文'
}
),
key: 'book_management'
},
// {
// label: "剪辑",
// key: "clip_options",
// children: [
// {
// label: () => h(
// RouterLink,
// {
// to: {
// name: "align_draft",
// }
// },
// { default: () => "剪映草稿对齐" }
// ),
// key: "align_draft"
// },
// {
// label: () => h(
// RouterLink,
// {
// to: {
// name: "add_draft",
// }
// },
// { default: () => "添加剪映草稿" }
// ),
// key: "add_draft"
// }
// ]
// },
{
label: () =>
h(
RouterLink,
{
to: {
name: 'lai_api'
}
},
{
default: () => 'API服务'
}
),
key: 'lai_api'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'TTS_Services'
}
},
{
default: () => '语音服务'
}
),
key: 'TTS_Services'
},
{
label: '设置',
key: 'setting',
children: [
{
label: () =>
h(
RouterLink,
{
to: {
name: 'global_setting'
}
},
{ default: () => '通用设置' }
),
key: 'global_setting'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'clip_setting'
}
},
{ default: () => '剪映设置' }
),
key: 'clip_setting'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'videogeneratesetting'
}
},
{ default: () => '生成视频设置' }
),
key: 'videogeneratesetting'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'sd_setting'
label: () =>
h(
RouterLink,
{
to: {
name: 'getframe'
}
},
class: 'sd_setting'
},
{ default: () => 'SD设置' }
),
key: 'sd_setting'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'mj_setting'
}
},
{ default: () => 'MJ设置' }
),
key: 'mj_setting'
}
]
},
{
label: () =>
h(
'div',
{
onClick: () => {
OpneBackTask()
},
style: 'font-weight: bold;'
{ default: () => '抽帧' }
),
key: 'backward_frame'
},
{
default: () => '后台任务'
label: () =>
h(
RouterLink,
{
to: {
name: 'copywriting'
}
},
{ default: () => '文案洗稿' }
),
key: 'copywriting'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'pushBackPrompt'
}
},
{ default: () => '反推提示词' }
),
key: 'push_back'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'regenerate'
}
},
{ default: () => '批次生成' }
),
key: 'regenerate'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'VideoGenerate'
}
},
{ default: () => '重绘/视频合成' }
),
key: 'VideoGenerate'
}
),
key: 'back_task'
]
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'book_management'
}
},
{
default: () => '聚合推文'
}
),
key: 'book_management'
},
// {
// label: "剪辑",
// key: "clip_options",
// children: [
// {
// label: () => h(
// RouterLink,
// {
// to: {
// name: "align_draft",
// }
// },
// { default: () => "剪映草稿对齐" }
// ),
// key: "align_draft"
// },
// {
// label: () => h(
// RouterLink,
// {
// to: {
// name: "add_draft",
// }
// },
// { default: () => "添加剪映草稿" }
// ),
// key: "add_draft"
// }
// ]
// },
{
label: () =>
h(
RouterLink,
{
to: {
name: 'lai_api'
}
},
{
default: () => 'API服务'
}
),
key: 'lai_api'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'TTS_Services'
}
},
{
default: () => '语音服务'
}
),
key: 'TTS_Services'
},
{
label: '设置',
key: 'setting',
children: [
{
label: () =>
h(
RouterLink,
{
to: {
name: 'global_setting'
}
},
{ default: () => '通用设置' }
),
key: 'global_setting'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'clip_setting'
}
},
{ default: () => '剪映设置' }
),
key: 'clip_setting'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'videogeneratesetting'
}
},
{ default: () => '生成视频设置' }
),
key: 'videogeneratesetting'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'sd_setting'
},
class: 'sd_setting'
},
{ default: () => 'SD设置' }
),
key: 'sd_setting'
},
{
label: () =>
h(
RouterLink,
{
to: {
name: 'mj_setting'
}
},
{ default: () => 'MJ设置' }
),
key: 'mj_setting'
}
// {
// label: () =>
// h(
// RouterLink,
// {
// to: {
// name: 'video_setting'
// }
// },
// { default: () => '视频设置' }
// ),
// key: 'video_setting'
// }
]
},
{
label: () =>
h(
'div',
{
onClick: () => {
OpneBackTask()
},
style: 'font-weight: bold;'
},
{
default: () => '后台任务'
}
),
key: 'back_task',
showMenu: true
}
]
if (window.config && window.config.showOriginal) {
return me
} else {
return me.filter((item) => item.showMenu || item.showMenu == undefined)
}
]
})
function renderIcon(icon) {
return () => h(NIcon, null, { default: () => h(icon) })
@@ -80,6 +80,14 @@
style="width: 200px"
/>
</n-form-item>
<n-form-item path="defaultImageMode" style="margin-left: 10px" label="默认图生视频模式">
<n-select
v-model:value="formValue.defaultVideoMode"
:options="defaultVideoModeOptions"
placeholder="选择默认图生视频模式"
style="width: 200px"
/>
</n-form-item>
</n-form-item>
<n-form-item path="gpt_setting" label="GPT设置">
<n-form-item path="gpt_business" label="GPT接口服务商">
@@ -195,6 +203,7 @@ import { isEmpty } from 'lodash'
import { useSoftwareStore } from '../../../../stores/software'
import TranslateSetting from './TranslateSetting.vue'
import { LaiAPIType } from '../../../../define/enum/softwareEnum'
import { ImageToVideoModels } from '@/define/enum/video'
let formRef = ref(null)
let softwareStore = useSoftwareStore()
@@ -217,7 +226,8 @@ let formValue = ref({
window_wh_bm_remember: window.config.window_wh_bm_remember,
laiApiSelect: window.config.laiApiSelect ? window.config.laiApiSelect : LaiAPIType.MAIN,
hdScale: window.config.hdScale ?? 2,
defaultImageMode: window.config.defaultImageMode ?? 'mj'
defaultImageMode: window.config.defaultImageMode ?? 'mj',
defaultVideoMode: window.config.defaultVideoMode ?? ImageToVideoModels.RUNWAY
})
let show = ref(false)
let gpt_options = ref([])
@@ -240,6 +250,20 @@ let hdSelectOptions = ref([
let dialog = useDialog()
let loading = ref(false)
let defaultImageModeOptions = ref([])
let defaultVideoModeOptions = ref([
{
label: 'Runway',
value: ImageToVideoModels.RUNWAY
},
{
label: 'Luma',
value: ImageToVideoModels.LUMA
},
{
label: '可灵',
value: ImageToVideoModels.KLING
}
])
/**
* 加载GPT的配置信息
*/
@@ -0,0 +1,14 @@
<template>
<n-card>
<div style="color: red">生成视频接口共用通用设置LAI API设置目前只支持LAI API</div>
<n-tabs type="line" animated>
<n-tab-pane name="runway-option" tab="Runway参数设置"> RUNWAY </n-tab-pane>
<n-tab-pane name="luma-option" tab="LUMA参数设置"> RUNWAY </n-tab-pane>
</n-tabs>
</n-card>
</template>
<script setup>
import { ref } from 'vue'
import { NCard, NTabs, NTabPane } from 'naive-ui'
</script>
+5
View File
@@ -88,6 +88,11 @@ const routes = [
name: 'mj_setting',
component: () => import('./components/Setting/MJSetting.vue')
},
{
path: '/video_setting',
name: 'video_setting',
component: () => import('./components/Setting/VideoSetting.vue')
},
{
path: '/book_management',
name: 'book_management',