Commit 4bb22ea8 by haojie

动作

parent ebbd8963
......@@ -22,7 +22,7 @@
justify-content: space-between;
align-items: center;
.custom-real-upload-component {
width: 572px;
// width: 572px;
height: 200px;
overflow-y: auto;
overflow-x: hidden;
......
......@@ -237,6 +237,7 @@ export default defineComponent({
url: '',
ref: null,
});
Curfile.status = 1;
openpercentage();
return alyOssUpload(props.config, file, UploadSuccessCallback, UploadErrorCallback, uuid);
};
......
......@@ -5,6 +5,8 @@
:autoWidth="autoWidth"
:placeholder="placeholder"
:multiple="multiple"
:align="align"
:clearable="clear"
@change="SelectChange"
:popupProps="{
overlayClassName: [className, 'custom-select-popup'],
......@@ -27,12 +29,16 @@ const props = withDefaults(
className?: string;
autoWidth?: boolean;
multiple?: boolean;
align?: string;
clear?: boolean;
}>(),
{
width: '50%',
placeholder: '请选择',
autoWidth: true,
multiple: false,
align: 'left',
clear: false,
},
);
const emit = defineEmits(['update:modelValue', 'change']);
......@@ -41,7 +47,11 @@ const SelectValue = ref(props.modelValue);
watch(
() => SelectValue.value,
(v) => {
if (v) {
emit('update:modelValue', v);
} else {
emit('update:modelValue', '');
}
},
);
watch(
......@@ -117,13 +127,22 @@ const SelectChange = (value: string | any) => {
background-color: #181818;
border: none;
border-radius: 8px;
.t-input__clear {
.t-icon {
color: #00dddd;
}
}
.t-input__inner {
text-align: center;
color: #ffffff;
&::placeholder {
color: #888fa1;
}
}
&:hover {
.t-fake-arrow {
color: #00dddd;
}
}
}
.t-is-focused {
box-shadow: 0px 0px 0px 1px #00dddd;
......
@import '@/style/variables';
.c-radio-group {
.da();
.default-radio {
.da();
.dot {
border-radius: 50%;
border: 1px solid #9f9f9f;
background: transparent;
width: 18px;
height: 18px;
cursor: pointer;
transition: all 0.2s;
}
.group-label {
font-size: @size-13;
color: #b4b4b4;
margin-left: 6px;
transition: all 0.2s;
}
}
.default-radio + .default-radio {
margin-left: 12px;
}
.radio-active {
.dot {
border-color: #04ae8a;
background: #04ae8a;
transition: all 0.2s;
}
.group-label {
color: #04ae8a;
transition: all 0.2s;
}
}
}
import './index.less';
import { computed, defineComponent } from 'vue';
export default defineComponent({
props: {
modelValue: [String, Number],
list: {
type: Array,
default: () => [],
},
cancel: {
type: Boolean,
default: false,
},
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const currentValue = computed({
get() {
return props.modelValue;
},
set(value) {
if (props.cancel && currentValue.value == value) {
emit('update:modelValue', '');
} else {
emit('update:modelValue', value);
}
},
});
const groupEvent = (item: any) => {
currentValue.value = item.value;
};
return () => (
<div class="c-radio-group">
{props.list.map((item: any) => (
<div key={item.value} class={['default-radio', currentValue.value == item.value ? 'radio-active' : '']}>
<div class="dot" onClick={groupEvent.bind(this, item)}></div>
<div class="group-label">{item.label}</div>
</div>
))}
</div>
);
},
});
import { computed, onBeforeUnmount, ref, watch } from 'vue';
import { useStore } from 'vuex';
import { createLiveKeys, createLiveVersion, filterFiled, getAudioStartTimeAndEndTime } from '@/service/CreateLive';
import { createLiveKeys, createLiveVersion, filterFiled, onAudioProcessed } from '@/service/CreateLive';
import { getLiveTtsCallback, createLiveTask, liveTts, liveTaskRegenerate } from '@/utils/api/userApi';
import { ecursionDeepCopy, isDev, show_message } from '@/utils/tool';
import { TableSortAsc, dimensionalConvert, ecursionDeepCopy, isDev, show_message } from '@/utils/tool';
import { useLiveInfoSubmit } from '@/hooks/useStoreCommit';
import { onUpdateLiveTask, audioStart, uploadToAly } from '@/service/Common';
import { v4 } from 'uuid';
import { useRoute, useRouter } from 'vue-router';
import routerConfig from '@/router/tool';
import useConfuse from '@/hooks/useConfuse';
import CustomException from '@/utils/error';
import { callPyjsInWindow, writeLog } from '@/utils/pyqt';
// 轮询处理文本脚本语音生成回调
......@@ -45,6 +44,7 @@ export const processTextCallback = () => {
tone_id: item[createLiveKeys.textTones],
content: list[i].content,
uuid: getTaskId(true),
id: i,
};
// 生成音频
await liveTts(params);
......@@ -125,8 +125,11 @@ export const processTextCallback = () => {
}
// 回首页
backHome();
loading.value = false;
return true;
}
loading.value = false;
return false;
} catch (e) {
writeLog({
name: '更新直播失败',
......@@ -134,6 +137,7 @@ export const processTextCallback = () => {
});
loading.value = false;
console.log(e);
return false;
}
}
};
......@@ -224,18 +228,21 @@ export const processTextCallback = () => {
});
if (res.code == 0) {
if (isDev()) {
for (let i = 0; i < 3; i++) {
for (let i = 0; i < 1; i++) {
let params = {
data: {
audio_address:
'http://nls-cloud-cn-shanghai.oss-cn-shanghai.aliyuncs.com/jupiter-flow/tmp/e56d8750eb3a44f9930f73703489acb1.wav?Expires=1692087730&OSSAccessKeyId=LTAIUpwNp2H7pBG5&Signature=FtKSld5Dn55po9GyTm%2BefRKmPqw%3D',
'http://yunyi-live.oss-cn-hangzhou.aliyuncs.com/upload/1/2023-08-16fcc74a89-d3f1-4545-a17c-d155dfa7978f.wav',
task_id: 0,
id: i,
},
};
res.data.push(params);
}
}
if (res.data.length) {
// 根据id升序排列
res.data = TableSortAsc(res.data, 'data.id');
console.log('音频任务回调成功');
console.log(res.data);
if (!isConfuse) {
......@@ -243,8 +250,8 @@ export const processTextCallback = () => {
// 关闭定时器
closeInterval();
let list = JSON.parse(JSON.stringify(createLiveInfo.value[createLiveKeys.textScriptList]));
let audio_list = [];
res.data.forEach((item: any) => {
for (let i = 0; i < res.data.length; i++) {
let item = res.data[i];
// 根据task_id更新数组对象
let data = item.data;
if (
......@@ -252,33 +259,41 @@ export const processTextCallback = () => {
data.audio_address &&
(typeof data.task_id === 'string' || typeof data.task_id === 'number')
) {
audio_list.push(data.audio_address);
console.log(list, 'list');
let index = list.findIndex((it: any) => it.task_id == data.task_id);
if (index !== -1) {
list[index].audio_address = data.audio_address;
// 音频处理
let resultList = await audioStart([data.audio_address], true);
// 转一维
resultList = dimensionalConvert(resultList);
let newList = resultList.map((row: any) => {
return {
content: row.content,
movement_type: list[index].movement_type,
movement_name: list[index].movement_name,
};
});
// 要提交的数组
list[index].newList = newList;
commitInfo({
[createLiveKeys.textScriptList]: list,
});
} else {
console.log('未找到对应的task_id');
}
} else {
show_message('缺少音频或id');
}
});
// 获取音频时长
let durationList = await getAudioStartTimeAndEndTime(res.data);
console.log(durationList, '不洗稿durationList');
if (!audio_list.length) {
throw new CustomException('没有要处理的音频');
}
let resultList = await audioStart(audio_list, true);
// 修改store的type_content
commitInfo({
[createLiveKeys.textScriptValue]: resultList,
});
console.log('执行完毕,准备提交');
// 提交
await submit(type);
let status = await submit(type);
if (!status) {
show_message('创建失败');
loading.value = false;
return;
}
// 需要洗稿
if (createLiveInfo.value[createLiveKeys.isDisorganize]) {
......@@ -306,28 +321,15 @@ export const processTextCallback = () => {
}
}
} else {
// 旧版洗稿回调,直播时用的是这个
if (res.data.length >= confuseLength) {
closeInterval();
let audio_list = [];
// 洗稿任务
res.data.forEach((item: any) => {
let data = item.data;
if (data && data.audio_address) {
audio_list.push(data.audio_address);
} else {
writeLog({
naem: 'useScript-洗稿缺少参数',
value: data,
});
show_message('洗稿缺少参数');
}
});
let resultList = await audioStart(audio_list, true);
let list = await onAudioProcessed(res, ecursionDeepCopy(createLiveInfo.value));
// 提交
if (customRegenerate) {
await customRegenerate(resultList);
await customRegenerate(list);
} else {
await regenerate(resultList, successCallback);
await regenerate(list, successCallback);
}
loading.value = false;
}
......
......@@ -167,12 +167,6 @@ const mergeCallback = (params: any) => {
onMounted(async () => {
// 将通知方法注入window
injectWindow('mergeCallback', mergeCallback);
// 传递用户token
try {
window.pyjs.setToken(getUserCookie());
} catch (e) {
console.error('没有pyjs');
}
// if (isDev()) {
// mergeCallback({
......
......@@ -147,6 +147,7 @@ const submitAudioTask = async (list: any[]) => {
tone_id: liveDetail.value.tone_id,
content: list[i].content,
uuid: currentConfuseId.value,
id: i,
};
// 生成音频
let res: any = await liveTts(params);
......@@ -524,7 +525,10 @@ const submitVideo = () => {
let index = realVideoList.value.findIndex((item: any) => !item.remove && !item.submit);
if (index !== -1) {
realVideoList.value[index].submit = true;
window.pyjs.run(realVideoList.value[index].url, routeQuery.id, route.query.window_index, index);
let list = realVideoList.value[index].url.map((item: any) => {
return item.url;
});
window.pyjs.run(list, routeQuery.id, route.query.window_index, index);
console.log(`本次提交-${index}`);
console.log(realVideoList.value);
} else {
......@@ -629,12 +633,6 @@ onMounted(async () => {
// 将通知方法注入window
injectWindow('mergeCallback', mergeCallback);
injectWindow('closeLive', closeLive);
// 传递用户token
try {
window.pyjs.setToken(getUserCookie());
} catch (e) {
console.error('没有pyjs');
}
// 获取后台互动
startLiveInterval();
......
import './index.less';
import { defineComponent, ref } from 'vue';
import { defineComponent } from 'vue';
import { LIVE_AUDIT_STATUS } from '@/service/Live';
import CardOneVue from '@/components/cardOne.vue';
import Button from '@/components/Button.vue';
......
......@@ -82,7 +82,7 @@ export default defineComponent({
url: item.url,
digital_man_id: currentCard.value,
};
if (!item.videoCover) {
if (item.videoCoverBlob) {
let result = await alyOssUpload(
ossConfig.value,
item.videoCoverBlob,
......@@ -111,17 +111,19 @@ export default defineComponent({
name: routerConfig.createAction.name,
query: {},
});
reset();
// 重新获取生产记录
getMovement();
}
} else {
let res: any = await createLiveMovement(list);
if (res.code == 0) {
show_message('提交成功,等待审核', 'success');
reset();
}
}
// 重新获取生产记录
getMovement();
}
}
globalLoading.value = false;
} catch (e) {
globalLoading.value = false;
......
......@@ -2,23 +2,34 @@
<Dialog v-model="visible" @confirm="confirm">
<div class="text-script-dialog-body">
<div class="input-box">
<div class="label">标题:</div>
<div class="label center">标题:</div>
<CustomInput v-model="titleValue" align="left" placeholder="请输入标题"></CustomInput>
</div>
<div class="input-box">
<div class="label">内容:</div>
<CustomTextarea v-model="contentValue"></CustomTextarea>
</div>
<div class="input-box">
<div class="label center">动作:</div>
<Select v-model="currentSelect" align="left" clear :options="actionList" :autoWidth="false"></Select>
<div class="radio-group-parent">
<CustomRadio v-model="currentRadio" :list="radioGroup" cancel></CustomRadio>
</div>
</div>
</div>
</Dialog>
</template>
<script lang="ts" setup>
import { watch, ref } from 'vue';
import { watch, ref, onMounted } from 'vue';
import Select from '@/components/Select.vue';
import Dialog from '@/components/Dialog.vue';
import CustomInput from '@/components/input/index.vue';
import CustomTextarea from '@/components/textarea.vue';
import { show_message } from '@/utils/tool';
import { getLiveMovementList } from '@/service/Common';
import { movementTypeStart, movementTypeEnd } from '@/service/CreateLive';
import CustomRadio from '@/components/radio';
const props = withDefaults(
defineProps<{
modelValue: boolean;
......@@ -34,7 +45,50 @@ const visible = ref(props.modelValue);
const titleValue = ref('');
const contentValue = ref('');
// 当前选择的动作
const currentSelect = ref('');
const actionList = ref([]);
const currentRadio = ref('');
// 单选列表
const radioGroup = [
{
label: '开头插入',
value: movementTypeStart,
},
{
label: '结尾插入',
value: movementTypeEnd,
},
];
const getAction = async () => {
let list = await getLiveMovementList(true);
actionList.value = list.map((item: any) => {
return {
label: item.name,
value: item.id,
url: item.url,
};
});
};
onMounted(() => {
getAction();
});
const confirm = () => {
let movement_name = '';
let movement_url = '';
if (currentSelect.value) {
// 找到对应的数据
let obj = actionList.value.find((item: any) => item.value == currentSelect.value);
if (obj) {
movement_name = obj.label;
movement_url = obj.url;
}
}
if (titleValue.value && contentValue.value) {
const { info } = props;
visible.value = false;
......@@ -42,6 +96,10 @@ const confirm = () => {
title: titleValue.value,
content: contentValue.value,
index: typeof info.index === 'number' ? info.index : false,
movement_id: currentSelect.value,
movement_type: currentRadio.value,
movement_name: movement_name,
movement_url: movement_url,
});
// 清空
......@@ -58,6 +116,13 @@ watch(
if (Object.keys(v).length) {
titleValue.value = v.title;
contentValue.value = v.content;
currentSelect.value = v.movement_id;
currentRadio.value = v.movement_type;
} else {
titleValue.value = '';
contentValue.value = '';
currentSelect.value = '';
currentRadio.value = '';
}
},
);
......@@ -88,6 +153,13 @@ watch(
font-size: @size-14;
white-space: nowrap;
}
.center {
.dja();
}
.radio-group-parent {
margin-left: 12px;
.da();
}
}
.input-box + .input-box {
margin-top: 12px;
......
<template>
<Dialog v-model="visible" @confirm="confirm" className="audio-script-dialog">
<div class="audio-script-dialog-body">
<div class="input-box">
<div class="label center">标题:</div>
<CustomInput v-model="titleValue" align="left" placeholder="请输入标题"></CustomInput>
</div>
<div class="input-box">
<div class="label">内容:</div>
<div class="value">
<MultipleUpload
v-model="audioList"
:config="ossConfig"
:computedDuration="true"
label="选择音频"
:accept="audioAccept"
></MultipleUpload>
</div>
</div>
<div class="input-box">
<div class="label center">动作:</div>
<Select v-model="currentSelect" align="left" clear :options="actionList" :autoWidth="false"></Select>
<div class="radio-group-parent">
<CustomRadio v-model="currentRadio" :list="radioGroup" cancel></CustomRadio>
</div>
</div>
</div>
</Dialog>
</template>
<script lang="ts" setup>
import { watch, ref, onMounted } from 'vue';
import Select from '@/components/Select.vue';
import Dialog from '@/components/Dialog.vue';
import CustomInput from '@/components/input/index.vue';
import { show_message } from '@/utils/tool';
import { getLiveMovementList } from '@/service/Common';
import { movementTypeStart, movementTypeEnd } from '@/service/CreateLive';
import CustomRadio from '@/components/radio';
import MultipleUpload from '@/components/MultipleUpload';
import { audioAccept } from '@/constants/token';
const props = withDefaults(
defineProps<{
modelValue: boolean;
info: any;
ossConfig: any;
}>(),
{},
);
const emit = defineEmits(['update:modelValue', 'submit']);
const visible = ref(props.modelValue);
// 标题
const titleValue = ref('');
// 音频列表
const audioList = ref([]);
// 当前选择的动作
const currentSelect = ref('');
const actionList = ref([]);
const currentRadio = ref('');
// 单选列表
const radioGroup = [
{
label: '开头插入',
value: movementTypeStart,
},
{
label: '结尾插入',
value: movementTypeEnd,
},
];
const getAction = async () => {
let list = await getLiveMovementList(true);
actionList.value = list.map((item: any) => {
return {
label: item.name,
value: item.id,
url: item.url,
};
});
};
onMounted(() => {
getAction();
});
const confirm = () => {
if (!titleValue.value) {
show_message('标题必填');
return;
}
if (!audioList.value.length) {
show_message('音频至少上传一个');
return;
}
let movement_name = '';
let movement_url = '';
if (currentSelect.value) {
// 找到对应的数据
let obj = actionList.value.find((item: any) => item.value == currentSelect.value);
if (obj) {
movement_name = obj.label;
movement_url = obj.url;
}
}
const { info } = props;
visible.value = false;
emit('submit', {
title: titleValue.value,
audioList: audioList.value,
index: typeof info.index === 'number' ? info.index : false,
movement_id: currentSelect.value,
movement_type: currentRadio.value,
movement_name: movement_name,
movement_url: movement_url,
});
// 清空
titleValue.value = '';
};
watch(
() => props.info,
(v) => {
if (Object.keys(v).length) {
titleValue.value = v.title;
audioList.value = v.audioList;
currentSelect.value = v.movement_id;
currentRadio.value = v.movement_type;
} else {
titleValue.value = '';
audioList.value = [];
currentSelect.value = '';
currentRadio.value = '';
}
},
);
watch(
() => visible.value,
(v) => {
emit('update:modelValue', v);
},
);
watch(
() => props.modelValue,
(v) => {
visible.value = v;
},
);
</script>
<style lang="less">
@import '@/style/variables';
.audio-script-dialog {
.t-dialog {
width: auto;
}
}
.audio-script-dialog-body {
margin: 16px 0;
.input-box {
display: flex;
.label {
color: #fff;
font-size: @size-14;
white-space: nowrap;
}
.center {
.dja();
}
.value {
flex: 1;
.custom-multiple-upload {
background-color: rgb(30, 30, 30);
width: 778px;
height: 256px;
.real-upload-content {
width: 100%;
.custom-real-upload-component {
width: 100%;
}
}
}
}
.radio-group-parent {
margin-left: 12px;
.da();
}
}
.input-box + .input-box {
margin-top: 12px;
}
}
</style>
......@@ -124,6 +124,15 @@
{{ item.content }}
</div>
</div>
<div class="action-box">
<div class="label">动作:</div>
<div class="value">
{{ item.movement_name }}
</div>
<div class="tag" v-if="item.movement_type">
{{ item.movement_type == 1 ? '开头插入' : '结尾插入' }}
</div>
</div>
</div>
</ScriptTemplate>
</template>
......@@ -137,34 +146,52 @@
<div class="script-setting-upload flex1 narrow-scrollbar" v-show="currentOption === scriptTypePhonetics">
<!-- edit -->
<template v-for="(item, index) in audioScriptList" :key="index">
<ScriptTemplate height="250px" :showEdit="false" @edit="uploadAudioEdit(index)" @delete="onDeleteAudio(index)">
<div class="script-template-body__audio-add">
<MultipleUpload
v-model="item.data"
:config="ossConfig"
:computedDuration="true"
label="选择音频"
:accept="audioAccept"
@change="uploadEdit"
></MultipleUpload>
<ScriptTemplate height="250px" @edit="uploadAudioEdit(item, index)" @delete="onDeleteAudio(index)">
<div class="script-template-body__text">
<div class="title-box">
<div class="label">标题:</div>
<div class="value">{{ item.title }}</div>
</div>
<div class="content-box">
<div class="label">内容:</div>
<div class="value narrow-scrollbar">
<div class="uploaded-audio-list">
<div v-for="row in item.audioList" :key="row.url">
<AudioSvg></AudioSvg>
<div>
{{ row.file.name }}
</div>
</div>
</div>
</div>
</div>
<div class="action-box">
<div class="label">动作:</div>
<div class="value">
{{ item.movement_name }}
</div>
<div class="tag" v-if="item.movement_type">
{{ item.movement_type == 1 ? '开头插入' : '结尾插入' }}
</div>
</div>
</div>
</ScriptTemplate>
</template>
<!-- create -->
<ScriptTemplate :showTool="false" height="250px">
<div class="script-template-body__audio-add">
<MultipleUpload
v-model="mp3UrlList"
:config="ossConfig"
:computedDuration="true"
label="选择音频"
:accept="audioAccept"
@change="createUploadFile"
></MultipleUpload>
<ScriptTemplate :showTool="false">
<div class="script-template-body__text-add" @click="addAudioScript">
<img :src="imgs.add" alt="" />
<div class="label">添加音频</div>
</div>
</ScriptTemplate>
</div>
<TextScriptDialog v-model="textScriptVisible" @submit="textScriptSubmit" :info="editTextInfo"></TextScriptDialog>
<AudioScriptDialog
v-model="audioScriptVisible"
:ossConfig="ossConfig"
@submit="audioScriptSubmit"
:info="editAudioInfo"
></AudioScriptDialog>
<ConfirmDialog
v-model="confirmDeleteVisible"
title="确定要删除该声音吗?"
......@@ -180,6 +207,7 @@
<script lang="tsx" setup>
import { computed, onMounted, reactive, ref, watch, toRaw } from 'vue';
import AudioSvg from '@/assets/svg/upload/audio.svg';
import Button from '@/components/Button.vue';
import MultipleUpload from '@/components/MultipleUpload';
import CheckBox from '@/components/CheckBox.vue';
......@@ -190,6 +218,7 @@ import Select from '@/components/Select.vue';
import SelectionPopup from '@/components/SelectionPopup.vue';
import { show_message, isDev, ecursionDeepCopy } from '@/utils/tool';
import { audioAccept } from '@/constants/token';
import AudioScriptDialog from './audioScriptDialog.vue';
import {
createLiveKeys,
scriptTypeList,
......@@ -201,6 +230,7 @@ import { useLiveInfoSubmit } from '@/hooks/useStoreCommit';
import { getUploadConfig, getTonesList } from '@/service/Common';
import { useStore } from 'vuex';
import { useRoute } from 'vue-router';
import { v4 } from 'uuid';
import useCopy from '@/hooks/useCopy';
const { doCopy } = useCopy();
......@@ -232,6 +262,7 @@ const audioScriptList = ref([]);
const scriptSettingText = ref<HTMLDivElement>();
// 文本编辑时的行信息
const editTextInfo = ref({});
const editAudioInfo = ref({});
// 文本脚本删除时选择的下标
const deleteTextId = ref();
// 确认删除弹窗
......@@ -266,6 +297,7 @@ const disabled = ref(true);
// 文本脚本弹窗
const textScriptVisible = ref(false);
const audioScriptVisible = ref(false);
// 阿里云上传配置
const ossConfig = ref({});
......@@ -284,20 +316,6 @@ const textareaValue = ref('');
const currentOption = ref(scriptTypeText);
// 音频脚本上传后
const createUploadFile = (list: any[], oldList: any[]) => {
// 添加到数组中
audioScriptList.value.push({
data: oldList,
});
// 提交到store
uploadChange();
setTimeout(() => {
// 清空当前url
mp3UrlList.value = [];
}, 0);
};
// 音频脚本编辑后
const uploadEdit = (list: any[], oldList: any[]) => {
uploadChange();
......@@ -315,18 +333,11 @@ const onDeleteAudio = (index: number) => {
};
// 编辑按钮 音频脚本
const uploadAudioEdit = (index: number) => {
// 调用对应的上传事件
if (uploadRef.value && uploadRef.value.length) {
let element: HTMLDivElement = uploadRef.value[index].$el;
if (element) {
// 找到上传元素
let clickElement = element.getElementsByClassName('custom-upload-click-box');
if (clickElement && clickElement.length) {
clickElement[0].click();
}
}
}
const uploadAudioEdit = (item: any, index: number) => {
item.index = index;
// 打开弹窗
editAudioInfo.value = item;
audioScriptVisible.value = true;
};
// 洗稿checkbox变化
......@@ -343,6 +354,11 @@ const addTextScript = () => {
textScriptVisible.value = true;
};
const addAudioScript = () => {
editAudioInfo.value = {};
audioScriptVisible.value = true;
};
// 编辑脚本
const editTextScript = (item: any, index: number) => {
item.index = index;
......@@ -350,6 +366,8 @@ const editTextScript = (item: any, index: number) => {
textScriptVisible.value = true;
};
// 编辑音频脚本
// 删除文本脚本
const deleteTextScript = (index: number) => {
deleteTextId.value = index;
......@@ -367,11 +385,15 @@ const confirmDeleteText = () => {
const textScriptSubmit = (params: any) => {
if (params.title && params.content) {
// 创建一个uuid
params.uuid = createLiveInfo.value[createLiveKeys.scriptUuid];
params.uuid = v4();
if (typeof params.index === 'number') {
// 编辑
textScriptList.value[params.index].title = params.title;
textScriptList.value[params.index].content = params.content;
textScriptList.value[params.index].movement_id = params.movement_id;
textScriptList.value[params.index].movement_type = params.movement_type;
textScriptList.value[params.index].movement_name = params.movement_name;
textScriptList.value[params.index].movement_url = params.movement_url;
// 其他参数都要清掉
// 任务id
textScriptList.value[params.index].task_id = '';
......@@ -385,6 +407,18 @@ const textScriptSubmit = (params: any) => {
}
};
// 提交音频脚本
const audioScriptSubmit = (params: any) => {
console.log(params);
if (typeof params.index === 'number') {
// 编辑模式
audioScriptList.value[params.index] = params;
} else {
audioScriptList.value.push(params);
}
uploadChange();
};
// 文本脚本内容提交到store
const submitTextScript = () => {
commitInfo({
......@@ -439,7 +473,6 @@ const updateInfo = (info: any) => {
currentOption.value = scriptTypeText;
if (type_content) {
// 内容
// textareaValue.value = type_content;
textScriptList.value = type_content;
}
// 洗稿
......@@ -457,16 +490,7 @@ const updateInfo = (info: any) => {
} else {
// 草稿
if (type_content) {
audioScriptList.value = type_content.map((item: any) => {
item.forEach((it: any) => {
it.audio_url = it.content;
it.status = true;
it.url = it.content;
});
return {
data: item,
};
});
audioScriptList.value = type_content;
} else {
audioScriptList.value = [];
}
......@@ -722,16 +746,20 @@ onMounted(async () => {
.flex1 {
flex: 1 1 auto;
}
.script-setting-text {
overflow-y: auto;
transition: 0.3s;
.custom-textarea-box {
.script-template-body__text-add {
height: 100%;
.custom-t-textarea {
height: 97%;
.t-textarea__inner {
height: 100% !important;
.dja();
flex-direction: column;
cursor: pointer;
img {
width: 40px;
height: 40px;
margin-bottom: 12px;
}
.label {
font-size: @size-18;
color: #b4b4b4;
font-weight: 600;
}
}
.script-template-body__text {
......@@ -746,6 +774,21 @@ onMounted(async () => {
white-space: nowrap;
}
}
.action-box {
color: #b4b4b4;
margin-top: 16px;
.da();
.tag {
margin-left: 20px;
border-radius: 4px;
background: #303030;
.dja();
color: #04ae8a;
font-size: @size-12;
font-weight: 600;
padding: 3px 10px 3px 11px;
}
}
.title-box {
color: #fff;
}
......@@ -755,26 +798,35 @@ onMounted(async () => {
flex: 1;
overflow: hidden;
.value {
width: 100%;
height: 100%;
word-break: break-all;
overflow-y: auto;
.uploaded-audio-list {
width: 100%;
.da();
flex-wrap: wrap;
row-gap: 20px;
margin-left: -20px;
& > * {
.dja();
flex-direction: column;
margin-left: 20px;
}
}
}
.script-template-body__text-add {
}
}
.script-setting-text {
overflow-y: auto;
transition: 0.3s;
.custom-textarea-box {
height: 100%;
.dja();
flex-direction: column;
cursor: pointer;
img {
width: 40px;
height: 40px;
margin-bottom: 12px;
.custom-t-textarea {
height: 97%;
.t-textarea__inner {
height: 100% !important;
}
.label {
font-size: @size-18;
color: #b4b4b4;
font-weight: 600;
}
}
}
......@@ -798,13 +850,6 @@ onMounted(async () => {
}
}
}
.custom-multiple-upload {
height: 100%;
background: transparent;
.custom-uploading-stauts {
padding-top: 12px;
}
}
}
}
}
......
......@@ -72,7 +72,7 @@ import ChoseDigitalPerson from './components/ChoseDigitalPerson.vue';
import HomeSvg from '@/assets/svg/createLive/home.svg';
import InteractSvg from '@/assets/svg/createLive/interact.svg';
import ScriptsSvg from '@/assets/svg/createLive/scripts.svg';
import { computed, onBeforeMount, ref, onBeforeUnmount, onActivated } from 'vue';
import { computed, onBeforeMount, ref, onBeforeUnmount, onActivated, toRaw } from 'vue';
import {
getElBounding,
show_message,
......@@ -224,6 +224,12 @@ const getEditInfo = async (id: any, type: string) => {
[createLiveKeys.interactiveLibrary]: res.data.interaction_ids,
};
if (res.data.type == '2') {
console.log(res.data.type_content);
res.data.type_content.forEach((item: any) => {
item.movement_id = item.extend.movement_id;
item.movement_type = item.extend.movement_type;
item.movement_name = item.extend.movement_name;
});
// 文本
params[createLiveKeys.textSoundColor] = res.data.phonetic_timbres_id;
params[createLiveKeys.textScriptList] = [res.data.type_content];
......@@ -232,7 +238,7 @@ const getEditInfo = async (id: any, type: string) => {
// 音频音色
params[createLiveKeys.phoneticsSoundColor] = res.data.phonetic_timbres_id;
params[createLiveKeys.phoneticsFile] = res.data.type_content;
params[createLiveKeys.audioScriptList] = mergeSameAudio(res.data.type_content);
params[createLiveKeys.audioScriptList] = mergeSameAudio(ecursionDeepCopy(toRaw(res.data.type_content)));
}
// 更新标题
if (res.data.name) {
......@@ -263,24 +269,7 @@ const getEditInfo = async (id: any, type: string) => {
// 音频音色
params[createLiveKeys.phoneticsSoundColor] = content.phonetic_timbres_id ? content.phonetic_timbres_id : '';
if (content.type_content) {
let newContent = ecursionDeepCopy(content.type_content);
// 过滤出child
newContent = newContent.map((item: any) => {
item.forEach((it: any) => {
it.children = [];
let children = it.content.split('|');
children.forEach((child) => {
let obj = {
audio_url: child,
};
it.children.push(obj);
});
});
return {
data: item,
};
});
params[createLiveKeys.audioScriptList] = newContent;
params[createLiveKeys.audioScriptList] = content.type_content;
}
}
// 更新标题
......@@ -377,6 +366,8 @@ const onSave = () => {
// 保存为草稿
const onSaveDrafts = async () => {
let params = filterFiled(getCreateLiveInfo());
// 单独修改草稿内容
params.type_content = createLiveInfo.value[createLiveKeys.audioScriptList];
try {
loading.value = true;
let res: any = await createDrafts(params);
......@@ -415,13 +406,8 @@ const audioScriptEditSubmit = async () => {
const editAudioSave = async () => {
try {
loading.value = true;
if (audioScriptVersion == 'v1') {
await audioSplit();
await audioScriptEditSubmit();
} else {
// v2
audioConvert('update');
}
} catch (e) {
writeLog({
name: 'createLive editAudioSave error',
......@@ -447,7 +433,9 @@ const submitTaskAndConfuse = async (type: string) => {
// 音调
tone_id: item[createLiveKeys.textTones],
content: row.content,
uuid: item[createLiveKeys.scriptUuid],
uuid: row.uuid,
parent_uuid: item[createLiveKeys.scriptUuid],
id: i,
};
// 生成音频
let res: any = await liveTts(params);
......@@ -483,57 +471,6 @@ const onEditSave = async () => {
}
};
// 音频切割v1
const audioSplit = async () => {
for (let i = 0; i < createLiveInfo.value[createLiveKeys.audioScriptList].length; i++) {
let item = createLiveInfo.value[createLiveKeys.audioScriptList][i];
for (let j = 0; j < item.data.length; j++) {
let row = item.data[j];
if (row.children && row.children.length > 1) {
// 编辑时没有修改参数
continue;
}
if (
row.duration &&
row.duration > audioSplitNum &&
row.content &&
row.content.indexOf('|') == -1 &&
row.old_content &&
!row.file?.raw
) {
console.log('大于5分钟,开始下载文件');
// 没有文件时,下载
let file = await getFile(row.old_content);
row.file = {};
row.file.raw = file;
console.log(row.file);
}
if (row.file && row.file.raw) {
// 文件时长
let fileDuration = await getDurationOfAudioFile(row.file.raw);
console.log(fileDuration, '文件时长');
if (fileDuration > audioSplitNum) {
// 开始切割前先判断文件类型
let result = await splitAudio(row.file.raw, audioSplitNum);
if (result.length) {
// 上传阿里云--加个判断,没有长度抛出异常
let alyList = await uploadToAly(result);
let list = [];
alyList.forEach((aly: any) => {
aly.forEach((alyRow: any) => {
list.push(alyRow.content);
});
});
// console.log(list, 'list');
// 一维数组
row.new_content = list.join('|');
}
}
}
}
}
};
// python转换音频回调
const convertCallback = (convertInfo: any) => {
console.log('转换回调', convertInfo);
......@@ -542,9 +479,9 @@ const convertCallback = (convertInfo: any) => {
let audioScriptList = createLiveInfo.value[createLiveKeys.audioScriptList];
// 根据切割前的链接匹配更新
for (let i = 0; i < audioScriptList.length; i++) {
let item = audioScriptList[i];
for (let j = 0; j < item.data.length; j++) {
let row = item.data[j];
let item = audioScriptList[i].audioList;
for (let j = 0; j < item.length; j++) {
let row = item[j];
if (getAudioUrl(row) == convertInfo.url) {
// 更新url
let key = getAudioUrlKey(row);
......@@ -579,9 +516,9 @@ const splitCallback = (splitInfo: any) => {
let audioScriptList = createLiveInfo.value[createLiveKeys.audioScriptList];
// 根据切割前的链接匹配更新
for (let i = 0; i < audioScriptList.length; i++) {
let item = audioScriptList[i];
for (let j = 0; j < item.data.length; j++) {
let row = item.data[j];
let item = audioScriptList[i].audioList;
for (let j = 0; j < item.length; j++) {
let row = item[j];
if (getAudioUrl(row) == splitInfo.url) {
row.new_content = splitInfo.list.join('|');
row.py_split_status = true;
......@@ -639,9 +576,9 @@ const audioConvert = async (type: string) => {
// 清空总任务数
audioConvertTaskTotal.value = 0;
for (let i = 0; i < list.length; i++) {
let item = list[i];
for (let j = 0; j < item.data.length; j++) {
let row = item.data[j];
let item = list[i].audioList;
for (let j = 0; j < item.length; j++) {
let row = item[j];
if (row.children && row.children.length > 1) {
// 编辑时没有修改参数
continue;
......@@ -651,12 +588,13 @@ const audioConvert = async (type: string) => {
let suffix = getFileSuffixInUrl(audio_url);
if (suffix === 'wav') {
console.log('是wav文件,开始转换', audio_url);
audioConvertToPython(row, type);
await audioConvertToPython(row, type);
} else {
console.log('不是wav', audio_url);
}
}
}
console.log(`共有${audioConvertTaskTotal.value}个文件要转换格式`);
if (audioConvertTaskTotal.value === 0) {
// 没有要转换的,直接切割
audioSplitV2(type);
......@@ -669,9 +607,9 @@ const audioSplitV2 = async (type: string) => {
// 清空总次数
audioSplitTaskTotal.value = 0;
for (let i = 0; i < list.length; i++) {
let item = list[i];
for (let j = 0; j < item.data.length; j++) {
let row = item.data[j];
let item = list[i].audioList;
for (let j = 0; j < item.length; j++) {
let row = item[j];
if (row.children && row.children.length > 1) {
// 编辑时没有修改参数
continue;
......@@ -746,13 +684,8 @@ const audioScriptLiveTaskSubmit = async () => {
const audioSubmit = async () => {
try {
loading.value = true;
if (audioScriptVersion == 'v1') {
await audioSplit();
await audioScriptLiveTaskSubmit();
} else {
// v2版本,提交到python处理
audioConvert('create');
}
} catch (e) {
writeLog({
name: 'createLive audioSubmit error',
......
......@@ -229,7 +229,9 @@ const startTest = async () => {
onMounted(() => {
// 发送token,即使重新登录也会回到首页再次发送
callPyjsInWindow('setToken', userToken.value);
callPyjsInWindow('setToken', {
token: userToken.value,
});
// 获取我的数字人
getList();
startTest();
......
......@@ -281,12 +281,6 @@ const mergeCallback = (params: any) => {
onMounted(async () => {
// 将通知方法注入window
injectWindow('mergeCallback', mergeCallback);
// 传递用户token
try {
window.pyjs.setToken(getUserCookie());
} catch (e) {
console.error('没有pyjs');
}
// if (isDev()) {
// mergeCallback({
......
......@@ -169,13 +169,10 @@ export const audioStart = async (list: any[], dimensional: boolean = true) => {
// 合并后的文件上传
if (split_list && split_list.length) {
let result = await uploadToAly(split_list);
console.log('上传完毕');
// 是否一维数组
if (dimensional) {
console.log(result);
return result;
} else {
console.log([result]);
return [result];
}
}
......@@ -236,10 +233,14 @@ export const uploadToAly = async (fileList: File[]) => {
};
// 获取动作列表
export const getLiveMovementList = async () => {
export const getLiveMovementList = async (filter: boolean = false) => {
try {
let res: any = await getLiveMovement();
if (res.code == 0) {
if (filter) {
// 过滤出成功的
return res.data.filter((item: any) => item.audit_status == LIVE_AUDIT_STATUS.LIVE_AUDIT_STATUS_FINISH);
}
return res.data;
}
return [];
......
import { mergedArray, isDev, show_message, dimensionalConvert, DataType } from '@/utils/tool';
import {
mergedArray,
isDev,
show_message,
dimensionalConvert,
DataType,
TableSortAsc,
ecursionDeepCopy,
} from '@/utils/tool';
import { liveContentRegenerateCallback, liveTts, getLiveTtsCallback, liveTaskRegenerate } from '@/utils/api/userApi';
import { audioStart } from '@/service/Common';
import store from '@/store';
......@@ -32,7 +40,7 @@ export const createLiveKeys = {
commentMethod: 'commentMethod', // 评论方式
interactiveLibrary: 'interactiveLibrary', // 互动库
isDisorganize: 'is_disorganize', // 是否洗稿
scriptUuid: 'script_uuid', // 文本脚本生成的uuid
scriptUuid: 'script_uuid', // 文本脚本生成的uuid
};
// 脚本类型
......@@ -53,19 +61,75 @@ export const scriptTypeList = [
export const typeTones = 1; // 音调
export const typeSoundColor = 2; // 音色
// 动作类型
export const movementTypeStart = 1; // 开头插入
export const movementTypeEnd = 2; // 结尾插入
// 合并同类项音频
export const mergeSameAudio = (content: any[]) => {
let list = mergedArray(content);
return list.map((item: any) => {
item.forEach((it: any) => {
if (it.audio_url) {
it.url = it.audio_url;
let list = [];
content.forEach((item: any, index: number) => {
let title = '';
let url = '';
let movement_name = '';
let movement_type = null;
let movement_id = null;
// 找到第一个old
let oldList = item.filter((it: any) => it.is_old);
if (oldList.length) {
title = oldList[0].extend.title;
url = oldList[0].audio_url;
movement_name = oldList[0].extend.movement_name;
movement_type = oldList[0].extend.movement_type;
movement_id = oldList[0].extend.movement_id;
}
// 获取audioList
let audioList = [];
oldList.forEach((it: any) => {
let params = {
file: {
name: it.name,
},
uuid: it.uuid,
url: it.audio_url,
duration: it.duration,
status: true,
};
audioList.push(params);
});
return {
data: item,
// 找出所有 is_old == 0的,根据uuid存放到 is_old ==1的对象中
item.forEach((it: any) => {
for (let i = 0; i < audioList.length; i++) {
let audioRow = audioList[i];
// 初始化children
if (!audioRow.children) {
audioRow.children = [];
}
if (it.uuid == audioRow.uuid && !it.is_old) {
let params = {
file: {
name: it.name,
},
duration: it.duration,
url: it.audio_url,
};
audioRow.children.push(params);
}
}
});
list.push({
audioList: audioList,
title: title,
url: url,
movement_name: movement_name,
movement_type: movement_type,
movement_id: movement_id,
});
});
return list;
};
// 获取洗稿回调
......@@ -91,23 +155,7 @@ export const onRewriteCallback = async (id: string) => {
//
res.data = [
{
content: `
大家好!欢迎来到今天的直播!我是你们的主持人,今天我将为大家带来一场精彩的直播节目。在这里,我们将分享一些有趣的内容,并回答你们的问题。
首先,让我们来聊一聊今天的主题。今天我们将重点聚焦在XX领域(根据直播主题填写),这是一个非常热门、有趣且前沿的领域。我们将了解最新的发展动态、分享一些实用的技巧,并回答你们提出的问题。
对于新加入直播的朋友,特别欢迎你们!如果你们有任何问题或者想要了解更多关于XX的信息,请随时在评论区留言,我将尽力回答你们的问题。
在这里,我们鼓励大家积极互动。请大家在评论区留下你们的想法、观点和问题。我会选择一些与主题相关的问题进行回答,在回答问题时我会尽量深入浅出,以确保每个人都能够理解。
也请大家相互尊重,遵守礼貌。如果有令人不舒服的言论或者不适当的内容,请及时举报,我们会及时处理。
接下来,让我们一起进入今天的正题吧!不管你是刚开始接触XX,还是已经有一定了解,请相信你们在这里能够获得更多知识、更多的收获。
再次感谢大家的到来和支持,让我们一起度过这个精彩的时刻!祝愿大家在本次直播中有所收获,也希望大家能够在评论区互相交流,共同进步。
谢谢大家!现在,让我们开始今天的直播吧!
`,
content: `大家好!欢迎来到今天的直播!我是你们的主持人,今天我将为大家带来一场精彩的直播节目`,
},
];
}
......@@ -147,6 +195,7 @@ export const submitAudioTask = async (list: any[], item: any, uuid: string) => {
tone_id: item[createLiveKeys.textTones],
content: list[i].content,
uuid: uuid,
id: i,
};
// 生成音频
await liveTts(params);
......@@ -208,25 +257,33 @@ export const filterFiled = (item: any, type: string = '') => {
// 脚本内容
if (item[createLiveKeys.scriptType] == scriptTypeText) {
// 文本
params.type_content = item[createLiveKeys.textScriptValue];
// 转换格式
// 先二维转一维
params.type_content = dimensionalConvert(params.type_content);
let list = [];
params.type_content.forEach((item: any) => {
list.push([item]);
// 文本内容--先转一维数组
let newList = dimensionalConvert(item[createLiveKeys.textScriptList]);
params.type_content = newList.map((row: any) => {
return {
movement: {
id: row.movement_id,
type: row.movement_type,
name: row.movement_name,
url: row.movement_url,
},
// 提交的数组
list: row.newList,
};
});
params.type_content = list;
console.log(params.type_content, '文本 type_content');
// 音色id
params.phonetic_timbres_id = item[createLiveKeys.textSoundColor];
// 文本内容--先转一维数组
let newList = dimensionalConvert(item[createLiveKeys.textScriptList]);
params.content = newList.map((row: any) => {
params.content = newList.map((row: any, index: number) => {
return {
title: row.title,
content: row.content,
movement_id: row.movement_id,
movement_type: row.movement_type,
movement_name: row.movement_name,
movement_url: row.movement_url,
};
});
} else {
......@@ -241,7 +298,7 @@ export const filterFiled = (item: any, type: string = '') => {
// 音频
params.type_content = item[createLiveKeys.audioScriptList].map((audioScript: any) => {
let list = [];
audioScript.data.forEach((it: any) => {
audioScript.audioList.forEach((it: any) => {
let params: any = {
content: '',
old_content: '',
......@@ -304,7 +361,19 @@ export const filterFiled = (item: any, type: string = '') => {
list.push(params);
});
return list;
let movement: any = {
id: audioScript.movement_id,
type: audioScript.movement_type,
name: audioScript.movement_name,
url: audioScript.movement_url,
};
return {
movement: movement,
list: list,
extend: {
title: audioScript.title,
},
};
});
console.log(params.type_content, '音频 type_content');
// 音色id
......@@ -375,6 +444,45 @@ export const getAudioStartTimeAndEndTime = async (list: any[]) => {
}
};
// 洗稿音频回调处理
export const onAudioProcessed = async (res: any, liveInfo: any) => {
let list = [];
for (let i = 0; i < res.data.length; i++) {
let item = res.data[i];
let data = item.data;
if (data && data.audio_address) {
let resultList = await audioStart([data.audio_address], true);
// 转一维
resultList = dimensionalConvert(resultList);
if (!resultList.length) {
show_message('洗稿失败');
writeLog('洗稿回调中音频下载失败-audioStart');
return;
}
let textScriptList = dimensionalConvert(liveInfo[createLiveKeys.textScriptList]);
list.push({
movement: {
id: textScriptList[i].movement_id,
type: textScriptList[i].movement_type,
name: textScriptList[i].movement_name,
url: textScriptList[i].movement_url,
},
list: resultList.map((row: any) => {
return {
content: row.content,
movement_type: textScriptList[i].movement_type,
movement_name: textScriptList[i].movement_name,
};
}),
});
} else {
console.log('洗稿缺少参数');
show_message('洗稿缺少参数');
}
}
return list;
};
// 洗稿获取音频回调
export const getAudioCallback = (audio_task_id: string, len: number, liveInfo: any, live_id: any) => {
let interval = null;
......@@ -393,38 +501,28 @@ export const getAudioCallback = (audio_task_id: string, len: number, liveInfo: a
});
if (res.code == 0) {
if (isDev()) {
let params = {
let params: any = {
data: {
audio_address:
'http://nls-cloud-cn-shanghai.oss-cn-shanghai.aliyuncs.com/jupiter-flow/tmp/e56d8750eb3a44f9930f73703489acb1.wav?Expires=1692087730&OSSAccessKeyId=LTAIUpwNp2H7pBG5&Signature=FtKSld5Dn55po9GyTm%2BefRKmPqw%3D',
'http://yunyi-live.oss-cn-hangzhou.aliyuncs.com/upload/1/2023-08-16fcc74a89-d3f1-4545-a17c-d155dfa7978f.wav',
task_id: 0,
},
};
for (let i = 0; i < 3; i++) {
for (let i = 0; i < 1; i++) {
params.data.id = i;
res.data.push(params);
}
}
if (res.data.length) {
// 根据id升序排列
res.data = TableSortAsc(res.data, 'data.id');
console.log('音频任务回调成功');
console.log(res.data);
if (res.data.length >= len) {
closeInterval();
let audioList = [];
res.data.forEach(async (item: any, index: number) => {
let data = item.data;
if (data && data.audio_address) {
audioList.push(data.audio_address);
} else {
console.log('洗稿缺少参数');
show_message('洗稿缺少参数');
}
});
// 获取音频时长
let durationList = await getAudioStartTimeAndEndTime(res.data);
console.log(durationList, '洗稿durationList');
let resultList = await audioStart(audioList, true);
let list = await onAudioProcessed(res, liveInfo);
// 提交
await regenerate(resultList, liveInfo, live_id);
await regenerate(list, liveInfo, live_id);
}
}
}
......
......@@ -81,10 +81,18 @@ export const TableSort = (list: any, field: string) => {
// 升序
export const TableSortAsc = (list: any, field: string) => {
let maxIndex, temp;
const getValue = (obj: any, path: string) => {
const keys = path.split('.');
let value = obj;
for (const key of keys) {
value = value[key];
}
return value;
};
for (let i = 0; i < list.length - 1; i++) {
maxIndex = i;
for (let j = i + 1; j < list.length; j++) {
if (list[j][field] < list[maxIndex][field]) {
if (getValue(list[j], field) < getValue(list[maxIndex], field)) {
maxIndex = j;
}
}
......@@ -453,7 +461,7 @@ export const mergedArray = (arr: any[], key: string = 'uuid', first: string = 'i
result.push(...existingObj);
}
newSubArray.forEach((obj) => {
const existingIndex = result.findIndex((it) => it.uuid === obj.uuid && !obj[first] && !obj.removed);
const existingIndex = result.findIndex((it) => it[key] === obj[key] && !obj[first] && !obj.removed);
if (existingIndex !== -1) {
// 标记
obj.removed = true;
......
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