All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 3s
486 lines
15 KiB
HTML
486 lines
15 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh">
|
||
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
||
<meta http-equiv="Pragma" content="no-cache" />
|
||
<meta http-equiv="expires" content="0" />
|
||
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximu-scale=1.0,user-scalable=no" />
|
||
<title>Yantu Digital human</title>
|
||
<link rel="icon" type="image/png" sizes="48x48" href="./yantu/favicon.png" />
|
||
<style>
|
||
:root {
|
||
--primary-100: #FF7F50;
|
||
--primary-200: #dd6236;
|
||
--primary-300: #8f1e00;
|
||
--accent-100: #8B4513;
|
||
--accent-200: #ffd299;
|
||
--text-100: #000000;
|
||
--text-200: #2c2c2c;
|
||
--bg-100: #F7EEDD;
|
||
--bg-200: #ede4d3;
|
||
--bg-300: #c4bcab;
|
||
}
|
||
|
||
html,
|
||
body {
|
||
margin: 0;
|
||
padding: 0;
|
||
font-family: 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||
background: var(--bg-100);
|
||
}
|
||
|
||
.modal {
|
||
position: fixed;
|
||
left: 0;
|
||
top: 0;
|
||
width: 100vw;
|
||
height: 100vh;
|
||
overflow: auto;
|
||
background: rgba(247, 238, 221, 0.95);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.modal-content {
|
||
background-color: transparent;
|
||
padding: 20px;
|
||
width: auto;
|
||
border-radius: 0;
|
||
box-shadow: none;
|
||
animation: fadeIn 0.3s ease-out;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; transform: translateY(-20px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
.container {
|
||
width: 100vw;
|
||
height: 100vh;
|
||
background: var(--bg-200);
|
||
}
|
||
|
||
.item {
|
||
margin-bottom: 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.item>span {
|
||
font-size: 24px;
|
||
font-weight: 600;
|
||
margin-bottom: 20px;
|
||
color: var(--text-200);
|
||
}
|
||
|
||
.input {
|
||
background: var(--bg-100);
|
||
height: 48px;
|
||
border: 1px solid var(--accent-100);
|
||
outline: none;
|
||
border-radius: 8px;
|
||
padding: 0 16px;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
font-size: 16px;
|
||
transition: all 0.3s ease;
|
||
color: var(--text-200);
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.input:focus {
|
||
border-color: var(--primary-100);
|
||
box-shadow: 0 0 0 3px var(--accent-200);
|
||
}
|
||
|
||
.btn {
|
||
border: none;
|
||
border-radius: 8px;
|
||
height: 48px;
|
||
background: var(--primary-100);
|
||
color: white;
|
||
cursor: pointer;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
transition: all 0.3s ease;
|
||
padding: 0 24px;
|
||
}
|
||
|
||
.btn:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 6px 8px rgba(0,0,0,0.15);
|
||
background: var(--primary-200);
|
||
}
|
||
|
||
.btn-config {
|
||
position: absolute;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: var(--accent-100);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 50%;
|
||
width: 40px;
|
||
height: 40px;
|
||
font-size: 20px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
transform: rotate(0deg);
|
||
}
|
||
|
||
.btn-config:hover {
|
||
transform: rotate(90deg);
|
||
background: var(--primary-200);
|
||
}
|
||
|
||
.btn:active {
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.statement {
|
||
position: absolute;
|
||
bottom: 160px;
|
||
left: 80%;
|
||
transform: translate(-50%);
|
||
color: #fff;
|
||
padding: 12px;
|
||
box-sizing: border-box;
|
||
text-align: center;
|
||
background-color: rgba(27, 160, 53, 0.5);
|
||
border-radius: 12px;
|
||
visibility: hidden;
|
||
z-index: 100000;
|
||
}
|
||
|
||
.statement:empty {
|
||
display: none;
|
||
}
|
||
|
||
.subtitle {
|
||
position: absolute;
|
||
bottom: 40%;
|
||
left: 50%;
|
||
transform: translate(-50%);
|
||
color: #000;
|
||
padding: 12px;
|
||
box-sizing: border-box;
|
||
text-align: center;
|
||
background-color: rgba(255, 255, 255, 0.5);
|
||
border-radius: 12px;
|
||
visibility: hidden;
|
||
z-index: 100000;
|
||
}
|
||
|
||
.subtitle:empty {
|
||
display: none;
|
||
}
|
||
|
||
|
||
</style>
|
||
<script src="https://cdn.bootcss.com/eruda/1.3.2/eruda.min.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/js-base64@3.7.2/base64.min.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/crypto-js.min.js"></script>
|
||
<script>eruda.init()</script>
|
||
<script src="./duix/duix.js"></script>
|
||
<!-- <script src="./yantu/yantoo-ava.js"></script> -->
|
||
</head>
|
||
|
||
<body>
|
||
<!-- 数字人容器 -->
|
||
<div class="container"></div>
|
||
<div class="statement"></div>
|
||
<div class="subtitle"></div>
|
||
<!-- <input class="input" style="display: block;border-color: #8f1e00;z-index: 999;" id="duihua" placeholder="输入对话" value=""> -->
|
||
|
||
<div id="modal" class="modal">
|
||
<div class="modal-content" style="text-align: center; padding: 20px; position: relative;">
|
||
<button id="configBtn" class="btn-config" onclick="toggleConfig()">⚙️</button>
|
||
<div id="configSection" style="display: none;">
|
||
<!-- <div style="margin-bottom: 8px; width: 100%; text-align: left; color: var(--text-200);">签名密钥</div> -->
|
||
<input class="input" style="display: none;" id="sign" placeholder="请输入您的签名密钥" value="" />
|
||
<input class="input" style="display: none;" id="audio" placeholder="音色" value="" />
|
||
<div style="margin-bottom: 8px; width: 100%; text-align: left; color: var(--text-200);">性别</div>
|
||
<select class="input" id="gender" style="margin-bottom: 16px;">
|
||
<option value="male" selected>男</option>
|
||
<option value="female" >女</option>
|
||
</select>
|
||
<select class="input" id="answer_type" style="margin-bottom: 16px;">
|
||
<option value="jiguang" selected>极光</option>
|
||
<option value="gongzheng">公证</option>
|
||
</select>
|
||
<div style="display: none;margin-bottom: 8px; width: 100%; text-align: left; color: var(--text-200);">会话ID</div>
|
||
<input class="input" id="conversationId" style="display: none;" placeholder="请输入会话ID" value="1920410565458886658" title="请输入会话ID" />
|
||
</div>
|
||
<button id="start" onclick="init()" class="btn" style="width: 200px; margin: 20px auto 0;">
|
||
<span>开始智能会话</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<script>
|
||
const duix = new DUIX()
|
||
|
||
const sex = {
|
||
"male": "1920410565458886658",
|
||
"female": "1933000305591988225"
|
||
}
|
||
let conversationL = ""
|
||
const sex_audio = {
|
||
"male": "gongzheng-v5",
|
||
"female": "presenter_female"
|
||
}
|
||
const audio = document.getElementById('audio')
|
||
// Add event listener for gender selection
|
||
document.getElementById('gender').addEventListener('change', function(e) {
|
||
const selectedGender = e.target.value;
|
||
const conversationIdInput = document.getElementById('conversationId');
|
||
|
||
conversationIdInput.value = sex[selectedGender];
|
||
audio.value = sex_audio[selectedGender]
|
||
});
|
||
|
||
let trigger_status = true
|
||
let end_talk = ""
|
||
|
||
// Set initial conversation ID based on default gender
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const genderSelect = document.getElementById('gender');
|
||
const conversationIdInput = document.getElementById('conversationId');
|
||
conversationIdInput.value = sex[genderSelect.value];
|
||
audio.value = sex_audio[genderSelect.value]
|
||
});
|
||
|
||
async function getToken() {
|
||
try {
|
||
const response = await fetch('https://srtc.yantootech.com/token', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
}
|
||
});
|
||
const data = await response.json();
|
||
return data.token;
|
||
} catch (error) {
|
||
console.error('Error getting token:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
function toggleConfig() {
|
||
const configSection = document.getElementById('configSection');
|
||
if (configSection.style.display === 'none') {
|
||
configSection.style.display = 'block';
|
||
} else {
|
||
configSection.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
async function init() {
|
||
let sign = document.querySelector('#sign').value;
|
||
const conversationId = document.querySelector('#conversationId').value;
|
||
|
||
if (!sign) {
|
||
try {
|
||
sign = await getToken();
|
||
document.querySelector('#sign').value = sign;
|
||
} catch (error) {
|
||
alert('获取token失败,请重试');
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (!conversationId) {
|
||
return alert('会话ID不能为空');
|
||
}
|
||
|
||
duix.on('error', data => {
|
||
console.error(data)
|
||
})
|
||
duix.on('intialSucccess', () => {
|
||
console.info('intialSucccess')
|
||
// 此时初始化成功,可调用start
|
||
duix.start({ conversationId, openAsr: true, useActSection: true }).then(res => {
|
||
console.info('start', res)
|
||
})
|
||
})
|
||
duix.on('bye', (data) => {
|
||
console.info('bye', data)
|
||
})
|
||
|
||
duix.on('progress', progress => {
|
||
console.info('progress', progress)
|
||
})
|
||
duix.on('show', () => {
|
||
console.info('show')
|
||
// 此时可确认视频已
|
||
document.querySelector('#modal').style.display = "none"
|
||
document.querySelector('.statement').style.visibility = "visible"
|
||
document.querySelector('.subtitle').style.visibility = "visible"
|
||
})
|
||
duix.on('openAsrSuccess', () => {
|
||
console.info('openAsrSuccess')
|
||
})
|
||
duix.on('asrClose', () => {
|
||
console.info('asrClose')
|
||
})
|
||
// duix.on('speakStart', (data) => {
|
||
// console.info('speakStart', data)
|
||
// })
|
||
// duix.on('speakEnd', (data) => {
|
||
// console.info('speakEnd', data)
|
||
// })
|
||
duix.on('speakSection', (data) => {
|
||
console.info('speakSection', data)
|
||
document.querySelector('.subtitle').innerText = data.content
|
||
})
|
||
duix.on('asrStop', (data) => {
|
||
console.info('asrStop', data)
|
||
document.querySelector('.statement').innerText = data.content;
|
||
});
|
||
duix.on('speakError', (data) => {
|
||
console.info('speakError', data)
|
||
})
|
||
duix.on('asrResult', async (data) => {
|
||
document.querySelector('.statement').innerText = ''
|
||
document.querySelector('.subtitle').innerText = ''
|
||
|
||
console.info('asrResult', data, trigger_status);
|
||
if (data == "小公" || data == "小共" || data == "小工"){
|
||
duix.break()
|
||
trigger_status = true
|
||
return
|
||
}
|
||
|
||
|
||
if (trigger_status) {
|
||
try {
|
||
trigger_status = false;
|
||
|
||
|
||
let plu = document.getElementById('answer_type')
|
||
api_data = {}
|
||
if (plu.value=="jiguang"){
|
||
api_data = {
|
||
"model": "bot-20250522162100-44785",
|
||
"llm_type": "ours",
|
||
"conversation_id": conversationL,
|
||
"audio": audio.value,
|
||
"stream": true,
|
||
"stream_options": {"include_usage": true},
|
||
"messages": [
|
||
{
|
||
"role": "system",
|
||
"content": "你是一个知识渊博的马克思主义研究者,能够深入且清晰地阐述马克思主义相关的理论、思想等内容,以通俗易懂的语言向用户讲解,还能结合实际案例来加深用户对马克思主义的理解。"
|
||
},
|
||
{
|
||
"role": "user",
|
||
"content": data
|
||
}
|
||
]
|
||
}
|
||
|
||
} else{
|
||
api_data = {
|
||
query: data,
|
||
response_mode: 'streaming',
|
||
user: 'SYS002',
|
||
conversation_id: conversationL,
|
||
audio: audio.value
|
||
}
|
||
|
||
}
|
||
const response = await fetch('https://srtc.yantootech.com/chat', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(api_data)
|
||
});
|
||
const reader = response.body.getReader();
|
||
const decoder = new TextDecoder();
|
||
let pre_talk = '';
|
||
|
||
|
||
while (true) {
|
||
const { value, done } = await reader.read();
|
||
if (done) break;
|
||
|
||
const chunk = decoder.decode(value);
|
||
const lines = chunk.split('\n');
|
||
|
||
for (const line of lines) {
|
||
if (line.startsWith('data:')) {
|
||
const responseData = JSON.parse(line.slice(5).trim());
|
||
|
||
|
||
if (responseData.audio_data) {
|
||
conversationL = responseData.conversation_id
|
||
console.log("执行输入answer")
|
||
pre_talk = responseData.answer
|
||
duix.speak({
|
||
content: responseData.answer,
|
||
audio: responseData.audio_data
|
||
})
|
||
|
||
}
|
||
|
||
|
||
// Handle end of stream
|
||
if (responseData.isEnd) {
|
||
console.log("pretalk : ", pre_talk, "---------", trigger_status)
|
||
end_talk = pre_talk
|
||
// trigger_status = true
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('Error in chat API:', error);
|
||
}
|
||
}
|
||
|
||
// test = [
|
||
// 'https://p3-bot-sign.byteimg.com/tos-cn-i-v4nquku3lp/0f1be92489b849f3b7568e9b17334e42.wav~tplv-v4nquku3lp-image.image?rk3s=68e6b6b5&x-expires=1752754758&x-signature=rXa5LFcZttKGZpHMVIP6SgZTsRI%3D','https://p6-bot-sign.byteimg.com/tos-cn-i-v4nquku3lp/9898244b02be4765a964cf4b3d4afcd0.wav~tplv-v4nquku3lp-image.image?rk3s=68e6b6b5&x-expires=1752754726&x-signature=%2BbIrCv%2B5yAUxZvNqBURCnSRVqWo%3D','https://p9-bot-sign.byteimg.com/tos-cn-i-v4nquku3lp/4bffded0446648a2aef1d2d7e59ddd0c.wav~tplv-v4nquku3lp-image.image?rk3s=68e6b6b5&x-expires=1752754642&x-signature=E5Dr2R%2Fhd%2Ft7K593ka393OlCPNg%3D']
|
||
// for (let index = 0; index < test.length; index++) {
|
||
// const element = test[index];
|
||
// duix.speak({content: "我", audio: element})
|
||
|
||
// }
|
||
});
|
||
|
||
duix.on('report', data => {
|
||
// console.info('report', data)
|
||
})
|
||
duix.on('speakEnd', async(data) => {
|
||
console.info('公证speakEnd: ', data["content"], "----------------", end_talk, "-----------------", trigger_status)
|
||
|
||
if(end_talk == data["content"]){
|
||
trigger_status = true
|
||
}
|
||
|
||
})
|
||
duix.init({
|
||
sign,
|
||
containerLable: '.container'
|
||
}).then(data => {
|
||
console.info('init', data)
|
||
})
|
||
}
|
||
|
||
window.addEventListener('beforeunload', function(event) {
|
||
if (duix) {
|
||
duix.destroy()
|
||
duix.stop()
|
||
}
|
||
});
|
||
|
||
</script>
|
||
</body>
|
||
|
||
</html> |