support 字幕

This commit is contained in:
turn_wind 2025-03-10 22:12:45 +08:00
parent 0738e12e55
commit 8c599fc9e4
4 changed files with 71 additions and 14 deletions

View File

@ -172,6 +172,33 @@
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 {
0% {
@ -206,9 +233,3 @@
transform-origin: center;
display: inline-block;
}

View File

@ -45,6 +45,9 @@
</div>
</div>
<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>

View File

@ -387,11 +387,18 @@ export class AITeacherApp {
const indices = segment.indices || [];
// 高亮当前段中的所有句子
let currentText = '';
indices.forEach(index => {
const sentenceElement = document.getElementById(`sentence-${index}`);
if (sentenceElement) {
sentenceElement.classList.add('highlight');
// 收集当前正在播放的句子文本
if (currentText) {
currentText += ' ';
}
currentText += this.sentences[index];
// 滚动到可见区域,但只在句子不在可见区域时执行
const container = document.getElementById('explanation-text');
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() {
@ -449,6 +468,14 @@ export class AITeacherApp {
// 清除高亮
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();
// 重置字幕
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) {
// 延迟1.5秒后跳转,给用户一个短暂的停顿时间

View File

@ -125,24 +125,22 @@ def text_to_speech(text, voice="zf_xiaoxiao", speed=1.5):
# 将句子按2句一组进行分组
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:
# 如果有两句,合并为一组
sentence_pairs.append({
"text": sentences[i] + " " + sentences[i+1],
"sentences": [sentences[i], sentences[i+1]],
"indices": [i, i+1]
})
i += 1
i += 2 # 正确跳过已合并的句子
else:
sentence_pairs.append({
"text": sentences[i],
"sentences": [sentences[i]],
"indices": [i]
})
i += 1 # 正常递增
# 存储所有音频段和时间戳
audio_segments = []