File size: 3,643 Bytes
acc335f
 
4a7f6be
 
 
 
acc335f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
let currentMode = 'normal'; // 🎬 默认为普通模式

// ⚡ 核心修正:自动适配本地与生产 (Hugging Face) 环境
const API_URL = window.location.hostname === "127.0.0.1" || window.location.hostname === "localhost" 
    ? "http://127.0.0.1:7860/theater" 
    : "https://jinv2-shensist-theater-matrix.hf.space/theater"; 

function setMode(mode) {
    currentMode = mode;
    const params = document.getElementById('director-params');
    
    // 💡 物理切换与动画控制
    if (mode === 'ai_factory') {
        params.style.display = 'flex';
        setTimeout(() => params.classList.add('active'), 10);
    } else {
        params.classList.remove('active');
        setTimeout(() => params.style.display = 'none', 500);
    }
    
    // UI 高亮切换
    document.getElementById('btn-normal').classList.toggle('active', mode === 'normal');
    document.getElementById('btn-factory').classList.toggle('active', mode === 'ai_factory');
    
    document.getElementById('subtitle').innerText = 
        mode === 'normal' ? "已进入:普通演示模式 (快速视觉校准)" : "已激活:AI 生产工厂 (准备深度对线)";
}

async function runTheater() {
    const topic = document.getElementById('input-topic').value;
    const model = document.getElementById('model-select').value;
    const apiKey = document.getElementById('user-api-key').value;
    const btn = document.getElementById('action-btn');
    const subtitle = document.getElementById('subtitle');

    if (currentMode === 'ai_factory' && !apiKey) {
        alert("⚠️ 架构师,请先填入 API KEY 以激活 AI 生产工厂!");
        return;
    }

    btn.disabled = true;
    subtitle.innerHTML = `<span style="color: #ff0000; font-weight: bold;">🎬 演员正在后台${currentMode === 'ai_factory' ? '对词(AI)' : '准备'},请稍候...</span>`;

    try {
        const res = await fetch(API_URL, {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({ topic, mode: currentMode, model, api_key: apiKey })
        });
        if (!res.ok) throw new Error('网络断电');
        const data = await res.json();
        
        // 🚀 灵魂演播开始!
        await playPerformance(data);
        
    } catch (err) {
        console.error("剧场故障:", err);
        document.getElementById('subtitle').innerHTML = '<span style="color:red">❌ 剧场意外断电,请检查后台。</span>';
    } finally {
        btn.disabled = false;
    }
}

async function playPerformance(lines) {
    const subtitle = document.getElementById('subtitle');
    subtitle.innerText = "🚀 灵魂演播开始!";

    for (let line of lines) {
        // 1. ⚡ 物理视觉切换
        document.querySelectorAll('.actor').forEach(a => a.classList.remove('active'));
        const activeActor = document.getElementById(line.actor_id);
        if (activeActor) activeActor.classList.add('active');

        // 2. ⚡ 播放内存音频
        await new Promise((resolve) => {
            let audio = new Audio(line.audio_data);
            audio.oncanplaythrough = () => {
                subtitle.style.color = line.actor_id === 'left' ? '#ff4444' : '#ff44ff';
                subtitle.innerText = line.text;
                audio.play().catch(resolve);
            };
            audio.onended = () => setTimeout(resolve, 500);
            audio.onerror = resolve;
        });
    }
    subtitle.style.color = "#fff";
    subtitle.innerText = "🎬 演播结束。";
    document.querySelectorAll('.actor').forEach(a => a.classList.remove('active'));
}