newtask.html
5.97 KB
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>新建视频任务 · MiniMax</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<header class="topbar">
<h1>MiniMax 视频生成测试台</h1>
<div class="spacer"></div>
<label class="small">fal API Key</label>
<input type="password" id="apiKeyInput" placeholder="key_id:key_secret" />
<button class="secondary" id="saveKeyBtn">保存</button>
</header>
<div class="layout">
<aside class="sidebar">
<div class="toolbar">
<input type="search" id="searchInput" placeholder="搜索 prompt..." />
<select id="statusFilter">
<option value="">全部状态</option>
<option value="Processing">生成中</option>
<option value="succeeded">成功</option>
<option value="failed">失败</option>
<option value="cancelled">取消</option>
</select>
<select id="modelFilter">
<option value="">全部模型</option>
<option value="MiniMax-H3">MiniMax-H3</option>
<option value="MiniMax-H3-Max">MiniMax-H3-Max</option>
</select>
<select id="sortSelect">
<option value="created_desc">创建时间 ↓</option>
<option value="created_asc">创建时间 ↑</option>
<option value="elapsed_asc">耗时 ↑</option>
<option value="elapsed_desc">耗时 ↓</option>
</select>
<button id="newTaskBtn">+ 新建任务</button>
</div>
<div class="task-list" id="taskList"></div>
</aside>
<main class="main">
<div class="card">
<div class="field">
<label>模型</label>
<select id="model">
<option value="MiniMax-H3-Max">MiniMax-H3-Max(速度优先,480P/768P,5-15s)</option>
<option value="MiniMax-H3">MiniMax-H3(画质更高,480P/768P/2K/4K,4-15s)</option>
</select>
</div>
<div class="field">
<label>Prompt</label>
<textarea id="prompt" placeholder="描述画面主体、动作、镜头运镜、氛围、风格;建议写清楚景别(远景/近景/特写)与光线质感。最多 7000 字符。"></textarea>
<div class="hint">参考 <a href="https://platform.minimax.cn/docs/guides/video-prompt" target="_blank">Prompt 撰写指南</a>:技术参数(比例/时长/分辨率)+ 叙事细节 + 镜头/声音方向,用自然语言描述,无需特殊标签。经 fal.ai 转发,暂不支持首帧/尾帧图片。</div>
</div>
<div class="row-3">
<div class="field">
<label>分辨率</label>
<select id="resolution"></select>
</div>
<div class="field">
<label>时长(秒)</label>
<input type="number" id="duration" />
</div>
<div class="field">
<label>画面比例</label>
<select id="ratio">
<option value="16:9" selected>16:9</option>
<option value="21:9">21:9</option>
<option value="4:3">4:3</option>
<option value="1:1">1:1</option>
<option value="3:4">3:4</option>
<option value="9:16">9:16</option>
</select>
</div>
</div>
<div id="errorBox" class="error-box" hidden></div>
<div class="form-actions">
<button id="submitBtn">提交任务</button>
<button class="secondary" onclick="location.href='index.html'">取消</button>
</div>
</div>
</main>
</div>
<script src="app.js"></script>
<script>
const apiKeyInput = document.getElementById("apiKeyInput");
apiKeyInput.value = getApiKey();
document.getElementById("saveKeyBtn").onclick = (e) => {
setApiKey(apiKeyInput.value);
const btn = e.currentTarget;
const old = btn.textContent;
btn.textContent = "已保存 ✓";
setTimeout(() => (btn.textContent = old), 1200);
};
initSidebar(null);
const modelSel = document.getElementById("model");
const resSel = document.getElementById("resolution");
const durInput = document.getElementById("duration");
function syncModelLimits() {
const limits = MODEL_LIMITS[modelSel.value];
resSel.innerHTML = limits.resolutions.map((r) => `<option value="${r}">${r}</option>`).join("");
durInput.min = limits.duration[0];
durInput.max = limits.duration[1];
durInput.value = limits.duration[0];
}
modelSel.addEventListener("change", syncModelLimits);
syncModelLimits();
document.getElementById("submitBtn").addEventListener("click", async () => {
const errorBox = document.getElementById("errorBox");
errorBox.hidden = true;
const prompt = document.getElementById("prompt").value.trim();
if (!prompt) {
errorBox.hidden = false;
errorBox.textContent = "请填写 Prompt";
return;
}
const limits = MODEL_LIMITS[modelSel.value];
const duration = Number(durInput.value);
if (duration < limits.duration[0] || duration > limits.duration[1]) {
errorBox.hidden = false;
errorBox.textContent = `时长需在 ${limits.duration[0]}-${limits.duration[1]} 秒之间`;
return;
}
const task = {
id: uid(),
model: modelSel.value,
falModel: limits.falModel,
prompt,
resolution: resSel.value,
duration,
ratio: document.getElementById("ratio").value,
status: "Processing",
requestId: null,
statusUrl: null,
resultUrl: null,
videoUrl: null,
error: null,
createdAt: Date.now(),
submittedAt: null,
completedAt: null,
lastRaw: null,
};
const btn = document.getElementById("submitBtn");
btn.disabled = true;
btn.textContent = "提交中...";
try {
const res = await submitVideoTask(task);
task.requestId = res.request_id;
task.statusUrl = res.status_url;
task.resultUrl = res.response_url;
task.submittedAt = Date.now();
task.lastRaw = res;
upsertTask(task);
location.href = `taskdetail.html?id=${task.id}`;
} catch (err) {
errorBox.hidden = false;
errorBox.textContent = err.message + (err.body ? "\n\n" + JSON.stringify(err.body, null, 2) : "");
btn.disabled = false;
btn.textContent = "提交任务";
}
});
</script>
</body>
</html>