support 字幕
This commit is contained in:
parent
0738e12e55
commit
8c599fc9e4
@ -172,6 +172,33 @@
|
|||||||
box-shadow: 0 0 3px rgba(74, 107, 223, 0.3);
|
box-shadow: 0 0 3px rgba(74, 107, 223, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.subtitle-container {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 15px;
|
||||||
|
margin: 10px 0 15px 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
color: white;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
min-height: 50px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle-container p {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-weight: 400;
|
||||||
|
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle-container.active {
|
||||||
|
background-color: rgba(74, 107, 223, 0.8);
|
||||||
|
box-shadow: 0 0 10px rgba(74, 107, 223, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0% {
|
0% {
|
||||||
@ -206,9 +233,3 @@
|
|||||||
transform-origin: center;
|
transform-origin: center;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -45,6 +45,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<canvas id="pdf-canvas"></canvas>
|
<canvas id="pdf-canvas"></canvas>
|
||||||
|
<div id="subtitle-container" class="subtitle-container">
|
||||||
|
<p id="subtitle-text">字幕将在播放讲解时显示在这里</p>
|
||||||
|
</div>
|
||||||
<div id="live2d-container" class="live2d-container"></div>
|
<div id="live2d-container" class="live2d-container"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -387,11 +387,18 @@ export class AITeacherApp {
|
|||||||
const indices = segment.indices || [];
|
const indices = segment.indices || [];
|
||||||
|
|
||||||
// 高亮当前段中的所有句子
|
// 高亮当前段中的所有句子
|
||||||
|
let currentText = '';
|
||||||
indices.forEach(index => {
|
indices.forEach(index => {
|
||||||
const sentenceElement = document.getElementById(`sentence-${index}`);
|
const sentenceElement = document.getElementById(`sentence-${index}`);
|
||||||
if (sentenceElement) {
|
if (sentenceElement) {
|
||||||
sentenceElement.classList.add('highlight');
|
sentenceElement.classList.add('highlight');
|
||||||
|
|
||||||
|
// 收集当前正在播放的句子文本
|
||||||
|
if (currentText) {
|
||||||
|
currentText += ' ';
|
||||||
|
}
|
||||||
|
currentText += this.sentences[index];
|
||||||
|
|
||||||
// 滚动到可见区域,但只在句子不在可见区域时执行
|
// 滚动到可见区域,但只在句子不在可见区域时执行
|
||||||
const container = document.getElementById('explanation-text');
|
const container = document.getElementById('explanation-text');
|
||||||
const elementRect = sentenceElement.getBoundingClientRect();
|
const elementRect = sentenceElement.getBoundingClientRect();
|
||||||
@ -405,6 +412,18 @@ export class AITeacherApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 更新字幕容器
|
||||||
|
const subtitleText = document.getElementById('subtitle-text');
|
||||||
|
const subtitleContainer = document.getElementById('subtitle-container');
|
||||||
|
|
||||||
|
if (subtitleText && currentText) {
|
||||||
|
subtitleText.textContent = currentText;
|
||||||
|
subtitleContainer.classList.add('active');
|
||||||
|
} else if (subtitleText) {
|
||||||
|
subtitleText.textContent = '字幕将在播放讲解时显示在这里';
|
||||||
|
subtitleContainer.classList.remove('active');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pauseAudio() {
|
pauseAudio() {
|
||||||
@ -449,6 +468,14 @@ export class AITeacherApp {
|
|||||||
|
|
||||||
// 清除高亮
|
// 清除高亮
|
||||||
this.clearHighlights();
|
this.clearHighlights();
|
||||||
|
|
||||||
|
// 重置字幕
|
||||||
|
const subtitleText = document.getElementById('subtitle-text');
|
||||||
|
const subtitleContainer = document.getElementById('subtitle-container');
|
||||||
|
if (subtitleText) {
|
||||||
|
subtitleText.textContent = '字幕将在播放讲解时显示在这里';
|
||||||
|
subtitleContainer.classList.remove('active');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,6 +493,14 @@ export class AITeacherApp {
|
|||||||
// 清除高亮
|
// 清除高亮
|
||||||
this.clearHighlights();
|
this.clearHighlights();
|
||||||
|
|
||||||
|
// 重置字幕
|
||||||
|
const subtitleText = document.getElementById('subtitle-text');
|
||||||
|
const subtitleContainer = document.getElementById('subtitle-container');
|
||||||
|
if (subtitleText) {
|
||||||
|
subtitleText.textContent = '字幕将在播放讲解时显示在这里';
|
||||||
|
subtitleContainer.classList.remove('active');
|
||||||
|
}
|
||||||
|
|
||||||
// 自动跳转到下一页(如果启用了自动翻页)
|
// 自动跳转到下一页(如果启用了自动翻页)
|
||||||
if (this.autoNavigate && this.pageNum < this.pdfDoc.numPages) {
|
if (this.autoNavigate && this.pageNum < this.pdfDoc.numPages) {
|
||||||
// 延迟1.5秒后跳转,给用户一个短暂的停顿时间
|
// 延迟1.5秒后跳转,给用户一个短暂的停顿时间
|
||||||
|
|||||||
10
server.py
10
server.py
@ -125,24 +125,22 @@ def text_to_speech(text, voice="zf_xiaoxiao", speed=1.5):
|
|||||||
|
|
||||||
# 将句子按2句一组进行分组
|
# 将句子按2句一组进行分组
|
||||||
sentence_pairs = []
|
sentence_pairs = []
|
||||||
for i in range(0, len(sentences)):
|
i = 0
|
||||||
|
while i < len(sentences):
|
||||||
if i + 1 < len(sentences) and len(sentences[i]) + len(sentences[i+1]) < 40:
|
if i + 1 < len(sentences) and len(sentences[i]) + len(sentences[i+1]) < 40:
|
||||||
# 如果有两句,合并为一组
|
|
||||||
sentence_pairs.append({
|
sentence_pairs.append({
|
||||||
"text": sentences[i] + " " + sentences[i+1],
|
"text": sentences[i] + " " + sentences[i+1],
|
||||||
"sentences": [sentences[i], sentences[i+1]],
|
"sentences": [sentences[i], sentences[i+1]],
|
||||||
"indices": [i, i+1]
|
"indices": [i, i+1]
|
||||||
})
|
})
|
||||||
i += 1
|
i += 2 # 正确跳过已合并的句子
|
||||||
else:
|
else:
|
||||||
sentence_pairs.append({
|
sentence_pairs.append({
|
||||||
"text": sentences[i],
|
"text": sentences[i],
|
||||||
"sentences": [sentences[i]],
|
"sentences": [sentences[i]],
|
||||||
"indices": [i]
|
"indices": [i]
|
||||||
})
|
})
|
||||||
|
i += 1 # 正常递增
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 存储所有音频段和时间戳
|
# 存储所有音频段和时间戳
|
||||||
audio_segments = []
|
audio_segments = []
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user