Commit 096e7203 by haojie

增加新版直播页面

parent ba2ce170
......@@ -52,6 +52,8 @@ import { onBeforeRouteLeave } from 'vue-router';
import { injectWindow } from '@/utils/pyqt';
import { scriptTypePhonetics } from '@/service/CreateLive';
// 再加一个动作视频标签
const props = withDefaults(
defineProps<{
playMainIndex: number | null;
......
......@@ -71,7 +71,7 @@ export const getRoutes = () => {
{
path: routerConfig.onlyVideoLive.path,
name: routerConfig.onlyVideoLive.name,
component: () => import('@/pages/OnlyVideoLive/index.vue'),
component: () => import('@/pages/OnlyVideoLive/indexV2.vue'),
meta: { title: 'snowhome', header: false, navbar: false },
},
// 只有人工回复的页面
......
......@@ -55,6 +55,8 @@ const imgs = {
mp4: new URL('../../assets/img/1.mp4', import.meta.url).href,
};
// 本次测试用的变量
const requestNum = ref(0);
const isFirst = ref(true);
// 定时检测python的方法是否注入成功
......@@ -434,19 +436,46 @@ const getDetail = async (type: string = '') => {
if (isDev()) {
// 创建url
res.data = {};
res.data.url = [
'http://yunyi-live.oss-cn-hangzhou.aliyuncs.com/live/output/87.mp4',
'http://yunyi-live.oss-cn-hangzhou.aliyuncs.com/live/output/87.mp4',
'http://yunyi-live.oss-cn-hangzhou.aliyuncs.com/live/output/87.mp4',
let list = [
{
url: 'http://yunyi-tiktok.oss-cn-shenzhen.aliyuncs.com/files/user/admin/e9f3d546-05f2-4dc1-a37d-d6c0ca960e43.mp4',
type: 1,
},
{
url: 'http://yunyi-tiktok.oss-cn-shenzhen.aliyuncs.com/files/user/admin/9604b4aa-e509-4f74-a73f-a0dc130f8f28.mp4',
type: 3,
// 是否需要播放
},
{
url: 'http://yunyi-tiktok.oss-cn-shenzhen.aliyuncs.com/files/user/admin/f2112aea-6f69-4403-acef-33d0fda7e736.mp4',
type: 1,
},
];
res.data.type_content = [
// 哪些时间段不能播放动作视频和互动视频
res.data.period = [
{
start: 1,
end: 5,
},
{
content: '测试文案提交',
start: 12,
end: 30,
},
];
res.data.url = list;
}
if (DataType(res.data, 'object') && res.data.url && res.data.url.length) {
//
// 初始化视频列表的状态
res.data.url.forEach((item: any) => {
// 是否播放完毕
item.status = false;
// 是否取走
item.remove = false;
// 是否正在播放
item.play = false;
});
// 取出所有 type==1 的主视频,计算总时长
realVideoList.value.push({
url: res.data.url,
// 合并后的地址
......@@ -459,27 +488,16 @@ const getDetail = async (type: string = '') => {
play: false,
// 是否已经提交给python
submit: false,
// 下一个视频的状态
// 下一个视频的状态(洗稿状态)
confuse: CONFUSE_STATUS.CONFUSE_STATUS_WAIT,
// 洗稿任务的uuid
uuid: v4(),
});
if (isDev()) {
let list = [
'http://yunyi-tiktok.oss-cn-shenzhen.aliyuncs.com/files/user/admin/e9f3d546-05f2-4dc1-a37d-d6c0ca960e43.mp4',
'http://yunyi-tiktok.oss-cn-shenzhen.aliyuncs.com/files/user/admin/9604b4aa-e509-4f74-a73f-a0dc130f8f28.mp4',
'http://yunyi-tiktok.oss-cn-shenzhen.aliyuncs.com/files/user/admin/f2112aea-6f69-4403-acef-33d0fda7e736.mp4',
];
// if (realVideoList.value.length > list.length) {
// console.log('不需要添加了');
// return;
// }
if (isFirst.value) {
mergeCallback({
// video: imgs.mp4,
video: list[0],
video: res.data.url[0].url,
index: realVideoList.value.length - 1,
childIndex: 0,
});
isFirst.value = false;
}
......
<template>
<div class="custom-start-only-video-page">
<div class="start-only-video-live">
<AddVideoPlay
v-model:playMainIndex="currentPlayMainIndex"
v-model:progress="progress"
:loading="loading"
:playId="addVideoId"
:liveDetail="liveDetail"
:video2="addVideo"
:mainVideoList="mainVideoList"
@playEnd="playEnd"
@currentTime="currentTimeChange"
@mainVideoListChange="mainVideoListChange"
></AddVideoPlay>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import AddVideoPlay from '@/components/AddVideoPlay.vue';
import { getLiveDetail, closeLiveTask } from '@/utils/api/userApi';
import { useRoute, useRouter } from 'vue-router';
import { show_message, isDev, DataType, randomIntFormList } from '@/utils/tool';
import { callPyjsInWindow, injectWindow } from '@/utils/pyqt';
import { getliveTaskReply, liveTts, getLiveTaskInfo, liveTaskRegenerate } from '@/utils/api/userApi';
import routerConfig from '@/router/tool';
import { useStore } from 'vuex';
import { v4 } from 'uuid';
import useConfuse from '@/hooks/useConfuse';
import { CONFUSE_STATUS } from '@/service/Live';
import { processTextCallback } from '@/hooks/useScript';
import { scriptTypeText } from '@/service/CreateLive';
import { writeLog } from '@/utils/pyqt';
const { currentConfuseId, confuseList, stopConfuse, openConfuseInterval } = useConfuse();
const { openInterval: confuseInterval } = processTextCallback();
const store = useStore();
const route = useRoute();
const router = useRouter();
const routeQuery = route.query;
const userInfo = computed(() => store.getters['user/userInfo']);
// 视频加载loading
const loading = ref(true);
const progress = ref(0);
// 剩余多少时长时开始洗稿并获取下一个视频
const esidueTime = 60 * 10;
const imgs = {
mp4: new URL('../../assets/img/1.mp4', import.meta.url).href,
};
// 本次测试用的变量
const requestNum = ref(0);
const isFirst = ref(true);
// 定时检测python的方法是否注入成功
let interval = null;
// 定时获取直播互动内容
let intervalLive = null;
// 定时获取后台主视频任务
let intervalMainVideo = null;
// 定时获取下一个要播放的主视频
let intervalLocalMainVideo = null;
// 主视频列表
const realVideoList = ref([]);
// 互动视频
const addVideo = ref(imgs.mp4);
// 互动视频列表
const addVideoList = ref([]);
// 互动视频当前播放id
const addVideoId = ref('');
// 直播详情
const liveDetail = ref({});
// 当前正在播放的主视频下标
const currentPlayMainIndex = ref(null);
// 主视频列表
const mainVideoList = ref([
{
name: 'mainVideo1',
// 播放链接
url: '',
// 当前视频是否播放完毕
playEnd: true,
// 是否正在播放
play: false,
show: true,
total: 0,
videoIndex: null,
},
{
name: 'mainVideo2',
// 播放链接
url: '',
// 当前视频是否播放完毕
playEnd: true,
play: false,
show: false,
total: 0,
videoIndex: null,
},
]);
// 主视频列表状态更新
const mainVideoListChange = (params: any) => {
if (typeof params.index === 'number' && !params.type) {
mainVideoList.value[params.index][params.key] = params.value;
if (params.key == 'show') {
// 列表其他index全部隐藏
for (let i = 0; i < mainVideoList.value.length; i++) {
let item = mainVideoList.value[i];
if (params.index != i) {
item.show = false;
}
}
}
}
// 更新视频列表
if (typeof params.index === 'number' && params.type === 'videoIndex') {
// 找到下标
let mainIndex = mainVideoList.value.findIndex((item: any, index: any) => index == params.index);
if (mainIndex !== -1) {
let videoIndex = mainVideoList.value[mainIndex].videoIndex;
realVideoList.value[videoIndex][params.videoKey] = params.videoValue;
console.log(realVideoList.value[videoIndex], '更新主视频列表');
}
}
};
const submitAudioTask = async (list: any[]) => {
for (let i = 0; i < list.length; i++) {
let params = {
// 音色
phonetic_timbres_id: liveDetail.value.phonetic_timbres_id,
// 音调
tone_id: liveDetail.value.tone_id,
content: list[i].content,
uuid: v4(),
parent_uuid: currentConfuseId.value,
id: i,
};
// 生成音频
await liveTts(params);
}
console.log('等待音频生成完成');
// 开始轮询
confuseInterval(true, '', false, currentConfuseId.value, regenerate, list.length);
};
// 洗稿列表变化
watch(
() => confuseList.value,
(v) => {
if (v.length) {
console.log('洗稿列表变化');
// 提交生成音频任务
submitAudioTask(v);
}
},
);
// 提交洗稿
const submitConfuse = async () => {
try {
// currentConfuseId.value = v4();
// let content = '';
// let contentList = liveDetail.value.type_content;
// if (contentList.length) {
// contentList.forEach((item: any) => {
// content += item.content;
// });
// console.log('提交洗稿任务');
// // 提交洗稿任务
// currentStartConfuse({
// content: content,
// task_id: currentConfuseId.value,
// id: route.query.id,
// });
// }
// 生成一个uid
currentConfuseId.value = v4();
openConfuseInterval(`${userInfo.value.id}-${liveDetail.value.id}`);
} catch (e) {
console.log(e);
}
};
// 找一个已经播放完毕的视频,加入video标签中
const findOneVideoInit = () => {
// 是否需要执行
let status = false;
let videoTagIndex = mainVideoList.value.findIndex((item: any, index: number) => index !== currentPlayMainIndex.value);
if (videoTagIndex !== -1) {
let hideVideo = mainVideoList.value[videoTagIndex];
// 隐藏的视频已经播放结束 且 视频列表中不存在有url,没取走的视频
let notRemove = realVideoList.value.find((item: any) => item.result && !item.remove);
if (!hideVideo.play && hideVideo.playEnd && !notRemove) {
console.log('需要重新入队');
status = true;
}
// start
if (status) {
// 当前显示的视频
let item = mainVideoList.value[currentPlayMainIndex.value];
const changeVideo = (url: string, index: number | boolean = false) => {
hideVideo.play = false;
hideVideo.playEnd = false;
hideVideo.url = url;
if (index !== false) {
hideVideo.videoIndex = index;
}
};
// 找到所有已经播放完毕的主视频
let playEndVideos = realVideoList.value.filter((row: any, index: number) => {
if (row.remove && item.result && item.status) {
return index;
}
});
if (playEndVideos.length) {
if (playEndVideos.length === 1) {
console.log('只有一条视频播放完毕,重新播放该视频');
changeVideo(item.url, item.videoIndex);
} else {
// 多条视频
// 随机下标
let num = randomIntFormList(playEndVideos);
console.log(`初始化第${num}个视频`);
// 链接是否一致
if (realVideoList.value[num].result === item.url) {
// 循环播放
changeVideo(item.url, item.videoIndex);
console.log('链接一致,等待重新播放');
} else {
// 更新当前视频标签的链接和状态
changeVideo(realVideoList.value[num].result, num);
console.log('成功加入队列');
}
}
} else {
// 将当前正在播放的视频放入video标签
changeVideo(item.url, item.videoIndex);
console.log('将当前视频传给hide的视频');
}
}
}
};
// 当前播放进度变化
const currentTimeChange = (index: number, value: number) => {
let row = mainVideoList.value[index];
// 剩余多少没有播放
let currentEsidueTime = row.total - value;
let currentVideoRow = realVideoList.value[row.videoIndex];
// 低于设置的值、没有开始洗稿、有文本内容 、必须是文本脚本
if (
currentEsidueTime < esidueTime &&
currentVideoRow.confuse === CONFUSE_STATUS.CONFUSE_STATUS_WAIT &&
liveDetail.value.type_content.length &&
typeof liveDetail.value.phonetic_timbres_id === 'number' &&
typeof liveDetail.value.tone_id === 'number' &&
liveDetail.value.is_disorganize &&
liveDetail.value.type == scriptTypeText &&
!stopConfuse.value
) {
console.log(row.videoIndex, '当前videoIndex');
currentVideoRow.confuse = CONFUSE_STATUS.CONFUSE_STATUS_PROGRESS;
console.log('直播开始洗稿');
// 开始洗稿
submitConfuse();
}
// 判断是否需要取之前的主视频
if (currentEsidueTime < 20 && typeof currentPlayMainIndex.value === 'number') {
findOneVideoInit();
}
};
// 互动视频播放结束
const playEnd = (id: any) => {
if (id) {
//
let index = addVideoList.value.findIndex((item: any) => item.id == id);
if (index !== -1) {
console.log('播放结束并更新状态', id);
addVideoList.value[index].play_status = true;
// 移出
addVideoList.value.splice(index, 1);
}
}
};
// 开启主视频任务定时器
const StartIntervalMainVideo = () => {
intervalMainVideo = window.setInterval(() => {
getDetail();
}, 10000);
};
// 关闭主视频任务定时器
const closeIntervalMainVideo = () => {
window.clearInterval(intervalMainVideo);
clearInterval(intervalMainVideo);
intervalMainVideo = null;
};
const stopInterval = () => {
window.clearInterval(interval);
clearInterval(interval);
interval = null;
};
// 开启本地主视频定时器
const openLocalMainVideoInterval = () => {
intervalLocalMainVideo = window.setInterval(() => {
takeMainVideoV2(false);
}, 5000);
};
// 关闭本地主视频定时器
const closeLocalMainVideoInterval = () => {
window.clearInterval(intervalLocalMainVideo);
clearInterval(intervalLocalMainVideo);
intervalLocalMainVideo = null;
};
// 获取最新的要播放的互动内容
const openInterval = () => {
interval = window.setInterval(() => {
// 找到第一个没有播放的
for (let i = 0; i < addVideoList.value.length; i++) {
let item = addVideoList.value[i];
if (item.play_status === false && item.remove) {
// 已有取走的任务正在执行
// console.log('已有取走的任务正在执行');
break;
}
if (item.play_status === false && item.remove === false) {
if (addVideo.value == item.reply_content) {
// 本次播放的视频与上次一致,通知视频模块重新播放
store.commit('live/videoReload');
// console.log('本次视频与上次一致');
}
addVideoList.value[i].remove = true;
addVideo.value = item.reply_content;
addVideoId.value = item.id;
break;
}
}
}, 100);
};
// 获取直播互动内容
const getLive = async () => {
try {
let res: any = await getliveTaskReply(route.query.id);
if (res.code == 0 && res.data && res.data.length) {
res.data.forEach((item: any) => {
item.play_status = false;
item.remove = false;
});
//id
// problem
// reply_content
addVideoList.value = addVideoList.value.concat(res.data);
}
} catch (e) {
writeLog({
name: 'getliveTaskReply 获取直播互动失败',
value: e,
});
console.log(e);
}
};
// 重新生成直播
const regenerate = async (list: any[]) => {
try {
let params = {
digital_man_id: liveDetail.value.digital_man_id,
name: liveDetail.value.name,
phonetic_timbres_id: liveDetail.value.phonetic_timbres_id,
tone_id: liveDetail.value.tone_id,
type: liveDetail.value.type,
};
params.type_content = list;
let res: any = await liveTaskRegenerate(liveDetail.value.id, params);
if (res.code == 0) {
//
console.log('重新生成直播,已提交');
}
} catch (e) {
writeLog({
name: 'only live regenerate error',
value: e,
});
console.log(e);
}
};
// 开启
const startLiveInterval = () => {
closeLiveInterval();
intervalLive = window.setInterval(() => {
getLive();
}, 3000);
};
// 关闭
const closeLiveInterval = () => {
window.clearInterval(intervalLive);
clearInterval(intervalLive);
intervalLive = null;
};
const getDetail = async (type: string = '') => {
if (!routeQuery.id) {
show_message('禁止访问');
return;
}
try {
let res: any = await getLiveDetail(routeQuery.id);
if (res.code == 0) {
if (isDev()) {
// 创建url
res.data = {};
let list = [
{
url: 'http://yunyi-tiktok.oss-cn-shenzhen.aliyuncs.com/files/user/admin/e9f3d546-05f2-4dc1-a37d-d6c0ca960e43.mp4',
type: 1,
},
{
url: 'http://yunyi-tiktok.oss-cn-shenzhen.aliyuncs.com/files/user/admin/9604b4aa-e509-4f74-a73f-a0dc130f8f28.mp4',
type: 3,
// 是否需要播放
},
{
url: 'http://yunyi-tiktok.oss-cn-shenzhen.aliyuncs.com/files/user/admin/f2112aea-6f69-4403-acef-33d0fda7e736.mp4',
type: 1,
},
];
// 哪些时间段不能播放动作视频和互动视频
res.data.period = [
{
start: 1,
end: 5,
},
{
start: 12,
end: 30,
},
];
res.data.url = list;
}
if (DataType(res.data, 'object') && res.data.url && res.data.url.length) {
// 初始化视频列表的状态
res.data.url.forEach((item: any) => {
// 是否播放完毕
item.status = false;
// 是否取走
item.remove = false;
// 是否正在播放
item.play = false;
});
// 取出所有 type==1 的主视频,计算总时长
realVideoList.value.push({
url: res.data.url,
// 合并后的地址
result: '',
// 是否播放完毕
status: false,
// 是否取走
remove: false,
// 是否正在播放
play: false,
// 是否已经提交给python
submit: false,
// 下一个视频的状态(洗稿状态)
confuse: CONFUSE_STATUS.CONFUSE_STATUS_WAIT,
});
if (isDev()) {
if (isFirst.value) {
mergeCallback({
video: res.data.url[0].url,
index: realVideoList.value.length - 1,
childIndex: 0,
});
isFirst.value = false;
}
} else {
// 通知python合并
submitVideo();
}
if (type === 'init') {
// 通知python刷新所有首页的直播列表
callPyjsInWindow('reloadLiveTaskList');
// 开播成功
router.replace({
path: routerConfig.onlyVideoLive.path,
name: routerConfig.onlyVideoLive.name,
query: {
...route.query,
is_live: '1',
},
});
}
} else {
console.log('直播没有返回值');
}
}
} catch (e) {
writeLog({
name: '获取直播链接失败',
value: e,
});
console.log(e);
}
};
// 视频列表提交到py
const submitVideo = () => {
try {
if (window.pyjs) {
if (window.pyjs.run) {
// 未取走且未提交过的视频
let index = realVideoList.value.findIndex((item: any) => !item.remove && !item.submit);
if (index !== -1) {
realVideoList.value[index].submit = true;
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 {
console.log('没有要提交的任务');
}
} else {
console.log('没有run方法');
}
} else {
show_message('empty-1 py');
}
} catch (e) {
console.log(e);
writeLog({
name: 'only submitVideo error',
value: e,
});
}
};
// 取主视频(v2)
const takeMainVideoV2 = (first: boolean = true) => {
// 找到第一个播放完毕的
let index = mainVideoList.value.findIndex((item: any) => item.playEnd);
if (index !== -1) {
let videoIndex = realVideoList.value.findIndex((item: any) => !item.remove && item.result && !item.status);
if (videoIndex !== -1) {
// 存入视频
mainVideoList.value[index].url = realVideoList.value[videoIndex].result;
// 更新状态
mainVideoList.value[index].playEnd = false;
mainVideoList.value[index].videoIndex = videoIndex;
// 视频已被取走
realVideoList.value[videoIndex].remove = true;
console.log(mainVideoList.value[index], '取出下一条要播放的视频', index, videoIndex);
if (first) {
// 视频加载完毕
loading.value = false;
}
}
}
};
// python 回调
const mergeCallback = (params: any) => {
try {
// console.log('python回调',params);
let index = params.index;
if (index) {
index = parseInt(index + '');
}
if (typeof index === 'number' && params.video) {
// 当前视频的返回结果
realVideoList.value[index].result = params.video;
// 首次播放
let list = realVideoList.value.filter((item: any) => item.remove === true);
if (!list.length) {
takeMainVideoV2();
// 首次回调后才开启主视频轮询
// 获取后台主视频
console.log('打开后台主视频轮询');
StartIntervalMainVideo();
}
} else {
console.log('回调格式错误');
console.log(params);
}
} catch (e) {
writeLog({
name: 'only mergeCallback error',
value: e,
});
console.log(e);
}
};
const getTone = async () => {
try {
let res = await getLiveTaskInfo(route.query.id);
if (res.code == 0) {
liveDetail.value = res.data;
}
} catch (e) {
console.log(e);
}
};
//
const closeLive = async () => {
try {
let res: any = await closeLiveTask(route.query.id);
if (res.code == 0) {
// 通知python刷新所有首页的直播列表
callPyjsInWindow('reloadLiveTaskList');
}
} catch (e) {
console.log(e);
}
};
onMounted(async () => {
// 将通知方法注入window
injectWindow('mergeCallback', mergeCallback);
injectWindow('closeLive', closeLive);
// 获取后台互动
startLiveInterval();
// 本地轮询获取要播放的互动视频
openInterval();
// 本地轮询获取要播放的主视频
openLocalMainVideoInterval();
// 可以开播
await getDetail('init');
// 获取音调和音色
getTone();
});
onBeforeUnmount(() => {
closeLiveInterval();
stopInterval();
closeIntervalMainVideo();
closeLocalMainVideoInterval();
});
</script>
<style lang="less">
@import '@/style/variables.less';
.custom-start-only-video-page {
display: flex;
width: 100% !important;
padding: 0 !important;
overflow: hidden;
& > * {
width: 100%;
background: #303030;
height: 100%;
}
.start-only-video-live {
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
}
}
</style>
......@@ -4,6 +4,7 @@ import { audioStart } from '@/service/Common';
import store from '@/store';
import { v4 } from 'uuid';
import { writeLog } from '@/utils/pyqt';
import { getDurationOfAudioFile } from '@/utils/audio';
/**
* 创建直播的版本
......@@ -56,6 +57,45 @@ export const typeSoundColor = 2; // 音色
export const movementTypeStart = 1; // 开头插入
export const movementTypeEnd = 2; // 结尾插入
// 计算音频块列表的开始时间和结束时间
export const getAudioStartTimeAndEndTime = async (list: any[]) => {
try {
let durationList = [];
for (let i = 0; i < list.length; i++) {
let data = list[i].data;
let params: any = {
audio_url: data.audio_address,
};
// 计算音频块的起始与结束时间点
let duration = await getDurationOfAudioFile(data.audio_address);
console.log(duration);
params.duration = duration;
// 默认值
params.start = 0;
params.end = duration;
durationList.forEach((it: any) => {
// 开始时间
params.start += it.duration;
});
durationList.push(params);
if (i !== 0) {
durationList.forEach((it: any, index: number) => {
// 不包括自己
if (index !== durationList.length - 1) {
// 结束时间
durationList[i].end += it.duration;
}
});
}
}
return durationList;
} catch (e) {
console.log(e);
}
};
// 合并同类项音频
export const mergeSameAudio = (content: any[]) => {
let list = [];
......
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