jytest/web/novel.html

235 lines
9.8 KiB
HTML
Raw 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.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>短剧/漫剧创作</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="header">
<h1>短剧/漫剧创作</h1>
</div>
<div class="container">
<!-- 阶段指示器 -->
<div class="stages" id="stages">
<div class="stage active" data-stage="script_analysis">1.剧本解析</div>
<div class="stage" data-stage="narration_design">2.旁白改编</div>
<div class="stage" data-stage="character_generate">3.角色生成</div>
<div class="stage" data-stage="scene_generate">4.场景生成</div>
<div class="stage" data-stage="storyboard_design">5.分镜设计</div>
<div class="stage" data-stage="shot_video_generate">6.分镜短片</div>
<div class="stage" data-stage="shot_video_compose">7.成片合成</div>
</div>
<!-- 阶段1: 剧本解析 -->
<div class="card" id="stage-script_analysis">
<div class="card-title">剧本解析</div>
<div class="form-group">
<label class="form-label">视觉风格 *</label>
<input type="text" class="form-input" id="visual_style" placeholder="如: 2D,国风,平涂 或 3D,CG动画,写实都市">
</div>
<div class="form-group">
<label class="form-label">视频比例 *</label>
<select class="form-select" id="video_ratio">
<option value="16:9">16:9 横屏</option>
<option value="9:16">9:16 竖屏</option>
</select>
</div>
<div class="form-group">
<label class="form-label">剧本文件URL *</label>
<input type="text" class="form-input" id="file_url" placeholder="公网可访问的剧本文件链接">
</div>
<div class="form-group">
<label class="form-label">文件类型 *</label>
<select class="form-select" id="file_type">
<option value="txt">TXT</option>
<option value="docx">DOCX</option>
</select>
</div>
<div class="form-group">
<label class="form-label">文件名 *</label>
<input type="text" class="form-input" id="file_name" placeholder="剧本文件名">
</div>
<button class="btn btn-primary btn-block" onclick="submitScriptAnalysis()">提交剧本解析</button>
<div id="result-script_analysis"></div>
</div>
<!-- 后续阶段表单(需要thread_id和asset_id) -->
<div class="card" id="stage-later" style="display:none">
<div class="card-title" id="later-stage-title">下一阶段</div>
<div class="form-group">
<label class="form-label">Thread ID</label>
<input type="text" class="form-input" id="thread_id" readonly>
</div>
<div class="form-group">
<label class="form-label">Asset ID</label>
<input type="text" class="form-input" id="asset_id" readonly>
</div>
<div id="extra-fields"></div>
<button class="btn btn-primary btn-block" id="submit-later-btn" onclick="submitLaterStage()">提交</button>
<div id="result-later"></div>
</div>
</div>
<nav class="nav">
<a href="index.html" class="nav-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
首页
</a>
<a href="novel.html" class="nav-item active">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
短剧
</a>
<a href="video.html" class="nav-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg>
视频
</a>
<a href="tasks.html" class="nav-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
任务
</a>
</nav>
<script src="js/app.js"></script>
<script>
let currentThreadID = '';
let currentAssetID = '';
let currentStage = 'script_analysis';
// 阶段顺序
const stageOrder = [
'script_analysis', 'narration_design', 'character_generate',
'scene_generate', 'storyboard_design', 'shot_video_generate', 'shot_video_compose'
];
function updateStages(completed) {
document.querySelectorAll('.stage').forEach(el => {
const s = el.dataset.stage;
const idx = stageOrder.indexOf(s);
const compIdx = stageOrder.indexOf(completed);
if (idx < compIdx) el.className = 'stage completed';
else if (s === completed) el.className = 'stage active';
else el.className = 'stage';
});
}
async function submitScriptAnalysis() {
const input = {
action_key: 'script_analysis',
visual_style: document.getElementById('visual_style').value,
video_ratio: document.getElementById('video_ratio').value,
file_url: document.getElementById('file_url').value,
file_type: document.getElementById('file_type').value,
file_name: document.getElementById('file_name').value
};
try {
const result = await API.post('/api/novel/submit', input);
if (result.data) {
currentThreadID = result.data.thread_id || '';
showAlert('剧本解析任务已提交!', 'success');
// 开始轮询
const taskID = result.data.task_id || '';
const runID = result.data.run_id || '';
pollTask(taskID || runID,
(id) => API.post('/api/novel/query', {task_id: taskID, run_id: runID}),
(res) => {
const status = res.data?.status;
if (status === 'SUCCESS') {
const novelData = res.data.novel_data;
if (novelData) {
currentThreadID = novelData.thread_id || currentThreadID;
try {
const respData = JSON.parse(novelData.resp_data || '{}');
currentAssetID = respData.OverviewAssetID || '';
} catch(e) {}
}
document.getElementById('thread_id').value = currentThreadID;
document.getElementById('asset_id').value = currentAssetID;
updateStages('script_analysis');
showNextStage('narration_design');
showAlert('剧本解析完成!可以继续下一阶段', 'success');
}
}
);
}
} catch(err) {
showAlert('提交失败: ' + err.message, 'error');
}
}
function showNextStage(stage) {
currentStage = stage;
const stageNames = {
'narration_design': '旁白改编',
'character_generate': '角色生成',
'scene_generate': '场景生成',
'storyboard_design': '分镜设计',
'shot_video_generate': '分镜短片生成',
'shot_video_compose': '成片合成'
};
document.getElementById('later-stage-title').textContent = stageNames[stage] || stage;
document.getElementById('stage-later').style.display = 'block';
// 分镜短片需要额外的shot_ids
let extra = '';
if (stage === 'shot_video_generate' || stage === 'shot_video_compose') {
extra = `<div class="form-group">
<label class="form-label">分镜ID列表(逗号分隔)</label>
<input type="text" class="form-input" id="shot_ids" placeholder="S1,S2,S3">
</div>`;
}
document.getElementById('extra-fields').innerHTML = extra;
updateStages(stage);
}
async function submitLaterStage() {
const input = {
action_key: currentStage,
thread_id: document.getElementById('thread_id').value,
asset_id: document.getElementById('asset_id').value
};
// 分镜阶段需要shot_ids
if (currentStage === 'shot_video_generate' || currentStage === 'shot_video_compose') {
const shotIds = document.getElementById('shot_ids')?.value;
if (shotIds) input.shot_ids = shotIds.split(',').map(s => s.trim());
}
try {
const result = await API.post('/api/novel/submit', input);
if (result.data) {
showAlert(getActionName(currentStage) + ' 任务已提交!', 'success');
const nTaskID = result.data.task_id || '';
const nRunID = result.data.run_id || '';
pollTask(nTaskID || nRunID,
(id) => API.post('/api/novel/query', {task_id: nTaskID, run_id: nRunID}),
(res) => {
const status = res.data?.status;
if (status === 'SUCCESS') {
const novelData = res.data.novel_data;
if (novelData) {
try {
const respData = JSON.parse(novelData.resp_data || '{}');
if (respData.OverviewAssetID) currentAssetID = respData.OverviewAssetID;
if (respData.StoryboardAssetID) currentAssetID = respData.StoryboardAssetID;
} catch(e) {}
document.getElementById('asset_id').value = currentAssetID;
}
updateStages(currentStage);
const idx = stageOrder.indexOf(currentStage);
if (idx < stageOrder.length - 1) {
showNextStage(stageOrder[idx + 1]);
}
showAlert(getActionName(currentStage) + ' 完成!', 'success');
}
}
);
}
} catch(err) {
showAlert('提交失败: ' + err.message, 'error');
}
}
</script>
</body>
</html>