zhizhi/modules/drama/server.js

122 lines
4.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const http = require('http');
const fs = require('fs');
const path = require('path');
const PORT = 3930;
const DEEPSEEK_KEY = 'sk-a9b69e9cd2dc4ca68d6aceaa84f22afb';
// Prompt 模板:三种模式
function buildPrompt(mode, input) {
const base = `你是一位专业的真人短剧编剧。请用以下专业格式输出剧本:
【人物表】
列出主要角色及简介
第X幕
场景 X · 地点 · 时间 · 内外景
[镜头提示] 动作/场景描述
角色名:(语气)"对白"
角色名:动作描述
[镜头切至] 下一个镜头
注意:
- 每场标注镜头语言(中景、特写、远景、跟拍等)
- 对白自然,符合人物性格
- 动作描写简洁有力
- 节奏紧凑3-5分钟短剧体量`;
switch (mode) {
case 'auto':
return `${base}\n\n用户提供的主题:「${input}\n请根据这个主题创作一个完整的真人短剧剧本。`;
case 'assist':
return `${base}\n\n用户提供的想法:「${input}\n请根据这个想法展开,创作一个完整的真人短剧剧本。`;
case 'polish':
return `${base}\n\n以下是用户提供的剧本草稿。请在不改变核心情节的前提下,优化对白、增强画面感、调整节奏:\n\n${input}`;
default:
return `${base}\n\n用户输入:「${input}`;
}
}
// 简易 HTML 注入
function serveStatic(res, filePath) {
const fullPath = path.join(__dirname, 'public', filePath);
try {
const data = fs.readFileSync(fullPath, 'utf-8');
const ext = path.extname(filePath);
const mime = { '.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript' }[ext] || 'text/plain';
res.writeHead(200, { 'Content-Type': mime });
res.end(data);
} catch {
res.writeHead(404);
res.end('Not found');
}
}
const server = http.createServer(async (req, res) => {
const url = new URL(req.url, `http://localhost:${PORT}`);
const pathname = url.pathname;
// CORS
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; }
// 静态文件
if (pathname === '/' || pathname === '/index.html') return serveStatic(res, 'index.html');
// 生成剧本 API
if (pathname === '/api/generate' && req.method === 'POST') {
let body = '';
req.on('data', c => body += c);
req.on('end', async () => {
try {
const { mode, input } = JSON.parse(body);
if (!input || input.trim().length < 2) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: '输入内容至少 2 个字符' }));
return;
}
const prompt = buildPrompt(mode || 'auto', input);
// 调 DeepSeek API
const apiRes = await fetch('https://api.deepseek.com/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${DEEPSEEK_KEY}`
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [
{ role: 'system', content: '你是一位专业的真人短剧编剧。输出格式严格遵循用户要求的剧本格式。' },
{ role: 'user', content: prompt }
],
temperature: 0.8,
max_tokens: 4096
})
});
const data = await apiRes.json();
const content = data.choices?.[0]?.message?.content || '生成失败,请重试';
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ success: true, content }));
} catch (e) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: e.message }));
}
});
return;
}
res.writeHead(404);
res.end('Not found');
});
server.listen(PORT, '0.0.0.0', () => {
console.log(`[drama] 启动成功 http://0.0.0.0:${PORT}`);
});