feat: refine money printer comfy template mode
This commit is contained in:
@@ -126,6 +126,7 @@ async fn resolve_auth(headers: &HeaderMap, state: &AppState) -> Result<MoneyPrin
|
|||||||
|
|
||||||
async fn status(State(state): State<AppState>, headers: HeaderMap) -> AppResult<Json<Value>> {
|
async fn status(State(state): State<AppState>, headers: HeaderMap) -> AppResult<Json<Value>> {
|
||||||
let auth = resolve_auth(&headers, &state).await?;
|
let auth = resolve_auth(&headers, &state).await?;
|
||||||
|
let cloud_backend = state.config.comfyui.uses_cloud_backend();
|
||||||
Ok(Json(json!({
|
Ok(Json(json!({
|
||||||
"success": true,
|
"success": true,
|
||||||
"mode": "boosapi-native",
|
"mode": "boosapi-native",
|
||||||
@@ -142,6 +143,15 @@ async fn status(State(state): State<AppState>, headers: HeaderMap) -> AppResult<
|
|||||||
"enabled": state.config.comfyui.uses_native_backend(),
|
"enabled": state.config.comfyui.uses_native_backend(),
|
||||||
"backend": format!("{:?}", state.config.comfyui.backend).to_ascii_lowercase(),
|
"backend": format!("{:?}", state.config.comfyui.backend).to_ascii_lowercase(),
|
||||||
"baseUrl": state.config.comfyui.target_base_url()
|
"baseUrl": state.config.comfyui.target_base_url()
|
||||||
|
},
|
||||||
|
"builtinCloudTemplate": {
|
||||||
|
"enabled": cloud_backend,
|
||||||
|
"label": "内置 Cloud Seedance 文生视频",
|
||||||
|
"nodeType": "ByteDanceTextToVideoNode",
|
||||||
|
"model": "seedance-1-0-lite-t2v-250428",
|
||||||
|
"duration": { "min": 3, "max": 12, "default": 5 },
|
||||||
|
"resolutions": ["480p", "720p", "1080p"],
|
||||||
|
"ratios": ["9:16", "16:9", "1:1", "4:3", "3:4", "21:9"]
|
||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
@@ -904,9 +914,18 @@ fn repair_bytedance_video_nodes(prompt: &mut Value) {
|
|||||||
}
|
}
|
||||||
if inputs
|
if inputs
|
||||||
.get("resolution")
|
.get("resolution")
|
||||||
.is_some_and(|value| !matches!(value.as_str(), Some("480p" | "720p" | "1080p")))
|
.is_some_and(|value| {
|
||||||
|
let normalized = value.as_str().unwrap_or_default().trim().to_ascii_lowercase();
|
||||||
|
!matches!(normalized.as_str(), "480p" | "720p" | "1080p")
|
||||||
|
})
|
||||||
{
|
{
|
||||||
inputs.insert("resolution".to_string(), json!("480p"));
|
inputs.insert("resolution".to_string(), json!("480p"));
|
||||||
|
} else if let Some(normalized) = inputs
|
||||||
|
.get("resolution")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.map(|value| value.trim().to_ascii_lowercase())
|
||||||
|
{
|
||||||
|
inputs.insert("resolution".to_string(), json!(normalized));
|
||||||
}
|
}
|
||||||
if inputs
|
if inputs
|
||||||
.get("aspect_ratio")
|
.get("aspect_ratio")
|
||||||
|
|||||||
@@ -1196,6 +1196,31 @@ export type MoneyPrinterJobsResponse = {
|
|||||||
jobs: MoneyPrinterJob[];
|
jobs: MoneyPrinterJob[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type MoneyPrinterStatusResponse = {
|
||||||
|
success: boolean;
|
||||||
|
mode: string;
|
||||||
|
keyName?: string;
|
||||||
|
capabilities?: string[];
|
||||||
|
comfy?: {
|
||||||
|
enabled?: boolean;
|
||||||
|
backend?: string;
|
||||||
|
baseUrl?: string;
|
||||||
|
};
|
||||||
|
builtinCloudTemplate?: {
|
||||||
|
enabled?: boolean;
|
||||||
|
label?: string;
|
||||||
|
nodeType?: string;
|
||||||
|
model?: string;
|
||||||
|
duration?: {
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
default?: number;
|
||||||
|
};
|
||||||
|
resolutions?: string[];
|
||||||
|
ratios?: string[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const api = {
|
export const api = {
|
||||||
// Sites
|
// Sites
|
||||||
getSites: async () => {
|
getSites: async () => {
|
||||||
@@ -2093,6 +2118,8 @@ export const api = {
|
|||||||
request<DigitalHumanJobResponse>(`/api/digital-human/jobs/${encodeURIComponent(id)}/cancel`, { method: "POST" }),
|
request<DigitalHumanJobResponse>(`/api/digital-human/jobs/${encodeURIComponent(id)}/cancel`, { method: "POST" }),
|
||||||
clearDigitalHumanJobs: () =>
|
clearDigitalHumanJobs: () =>
|
||||||
request<{ success: boolean; cleared: number }>("/api/digital-human/jobs/clear", { method: "POST" }),
|
request<{ success: boolean; cleared: number }>("/api/digital-human/jobs/clear", { method: "POST" }),
|
||||||
|
getMoneyPrinterStatus: () =>
|
||||||
|
request<MoneyPrinterStatusResponse>("/api/money-printer/status"),
|
||||||
createMoneyPrinterJob: (data: any) =>
|
createMoneyPrinterJob: (data: any) =>
|
||||||
request<MoneyPrinterJobResponse>("/api/money-printer/jobs", {
|
request<MoneyPrinterJobResponse>("/api/money-printer/jobs", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@@ -2196,6 +2223,8 @@ export const userApi = {
|
|||||||
userRequest<DigitalHumanJobResponse>(`/api/digital-human/jobs/${encodeURIComponent(id)}/cancel`, { method: "POST" }),
|
userRequest<DigitalHumanJobResponse>(`/api/digital-human/jobs/${encodeURIComponent(id)}/cancel`, { method: "POST" }),
|
||||||
clearDigitalHumanJobs: () =>
|
clearDigitalHumanJobs: () =>
|
||||||
userRequest<{ success: boolean; cleared: number }>("/api/digital-human/jobs/clear", { method: "POST" }),
|
userRequest<{ success: boolean; cleared: number }>("/api/digital-human/jobs/clear", { method: "POST" }),
|
||||||
|
getMoneyPrinterStatus: () =>
|
||||||
|
userRequest<MoneyPrinterStatusResponse>("/api/money-printer/status"),
|
||||||
createMoneyPrinterJob: (data: any) =>
|
createMoneyPrinterJob: (data: any) =>
|
||||||
userRequest<MoneyPrinterJobResponse>("/api/money-printer/jobs", {
|
userRequest<MoneyPrinterJobResponse>("/api/money-printer/jobs", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|||||||
@@ -9351,6 +9351,11 @@ textarea {
|
|||||||
background: var(--color-bg-card);
|
background: var(--color-bg-card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.money-printer-template-meta.is-warning {
|
||||||
|
border-color: color-mix(in srgb, var(--color-warning) 38%, var(--color-border-light));
|
||||||
|
background: color-mix(in srgb, var(--color-warning) 8%, var(--color-bg-card));
|
||||||
|
}
|
||||||
|
|
||||||
.money-printer-template-meta strong {
|
.money-printer-template-meta strong {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { api, userApi, type MoneyPrinterJob, type WorkflowSummary } from '../api.js';
|
import { api, userApi, type MoneyPrinterJob, type MoneyPrinterStatusResponse, type WorkflowSummary } from '../api.js';
|
||||||
import type { SessionAudience } from '../authSession.js';
|
import type { SessionAudience } from '../authSession.js';
|
||||||
|
|
||||||
const STYLE_PRESETS = [
|
const STYLE_PRESETS = [
|
||||||
@@ -225,6 +225,10 @@ function parseJsonInput(value: string, label: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const BUILTIN_CLOUD_TEMPLATE_VALUE = '__builtin_cloud_seedance__';
|
const BUILTIN_CLOUD_TEMPLATE_VALUE = '__builtin_cloud_seedance__';
|
||||||
|
const DEFAULT_BUILTIN_CLOUD_DURATION = 5;
|
||||||
|
const DEFAULT_BUILTIN_CLOUD_RESOLUTION = '480p';
|
||||||
|
const DEFAULT_CLOUD_RATIOS = ['9:16', '16:9', '1:1'];
|
||||||
|
const DEFAULT_CLOUD_RESOLUTIONS = ['480p', '720p', '1080p'];
|
||||||
|
|
||||||
export default function MoneyPrinter() {
|
export default function MoneyPrinter() {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
@@ -247,14 +251,40 @@ export default function MoneyPrinter() {
|
|||||||
const [templateId, setTemplateId] = useState('');
|
const [templateId, setTemplateId] = useState('');
|
||||||
const [showAdvancedComfy, setShowAdvancedComfy] = useState(false);
|
const [showAdvancedComfy, setShowAdvancedComfy] = useState(false);
|
||||||
const [jobs, setJobs] = useState<MoneyPrinterJob[]>([]);
|
const [jobs, setJobs] = useState<MoneyPrinterJob[]>([]);
|
||||||
|
const [status, setStatus] = useState<MoneyPrinterStatusResponse | null>(null);
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
const isCloudComfy = status?.comfy?.backend === 'cloud' && status?.builtinCloudTemplate?.enabled !== false;
|
||||||
|
const cloudDurationMin = status?.builtinCloudTemplate?.duration?.min ?? 3;
|
||||||
|
const cloudDurationMax = status?.builtinCloudTemplate?.duration?.max ?? 12;
|
||||||
|
const cloudDefaultDuration = status?.builtinCloudTemplate?.duration?.default ?? DEFAULT_BUILTIN_CLOUD_DURATION;
|
||||||
|
const cloudResolutions = useMemo(
|
||||||
|
() => status?.builtinCloudTemplate?.resolutions?.length
|
||||||
|
? status.builtinCloudTemplate.resolutions
|
||||||
|
: DEFAULT_CLOUD_RESOLUTIONS,
|
||||||
|
[status?.builtinCloudTemplate?.resolutions],
|
||||||
|
);
|
||||||
|
const cloudRatios = useMemo(
|
||||||
|
() => status?.builtinCloudTemplate?.ratios?.length
|
||||||
|
? status.builtinCloudTemplate.ratios
|
||||||
|
: DEFAULT_CLOUD_RATIOS,
|
||||||
|
[status?.builtinCloudTemplate?.ratios],
|
||||||
|
);
|
||||||
|
|
||||||
const loadJobs = useCallback(async () => {
|
const loadJobs = useCallback(async () => {
|
||||||
const payload = await client.getMoneyPrinterJobs({ limit: 30 });
|
const payload = await client.getMoneyPrinterJobs({ limit: 30 });
|
||||||
setJobs(Array.isArray(payload.jobs) ? payload.jobs : []);
|
setJobs(Array.isArray(payload.jobs) ? payload.jobs : []);
|
||||||
}, [client]);
|
}, [client]);
|
||||||
|
|
||||||
|
const loadStatus = useCallback(async () => {
|
||||||
|
const payload = await client.getMoneyPrinterStatus();
|
||||||
|
setStatus(payload);
|
||||||
|
}, [client]);
|
||||||
|
|
||||||
|
const refresh = useCallback(async () => {
|
||||||
|
await Promise.all([loadJobs(), loadStatus()]);
|
||||||
|
}, [loadJobs, loadStatus]);
|
||||||
|
|
||||||
const loadWorkflows = useCallback(async () => {
|
const loadWorkflows = useCallback(async () => {
|
||||||
const payload = await client.getWorkflows({ include_public: true });
|
const payload = await client.getWorkflows({ include_public: true });
|
||||||
const items = Array.isArray(payload.items) ? payload.items : [];
|
const items = Array.isArray(payload.items) ? payload.items : [];
|
||||||
@@ -262,19 +292,44 @@ export default function MoneyPrinter() {
|
|||||||
setWorkflows(videoItems);
|
setWorkflows(videoItems);
|
||||||
setTemplateId((current) => {
|
setTemplateId((current) => {
|
||||||
if (current && videoItems.some((item) => item.publicId === current)) return current;
|
if (current && videoItems.some((item) => item.publicId === current)) return current;
|
||||||
|
if (isCloudComfy) return BUILTIN_CLOUD_TEMPLATE_VALUE;
|
||||||
return videoItems.find((item) => item.isTemplate || item.visibility === 'template' || item.visibility === 'public')?.publicId
|
return videoItems.find((item) => item.isTemplate || item.visibility === 'template' || item.visibility === 'public')?.publicId
|
||||||
|| videoItems[0]?.publicId
|
|| videoItems[0]?.publicId
|
||||||
|| '';
|
|| '';
|
||||||
});
|
});
|
||||||
}, [client]);
|
}, [client, isCloudComfy]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Promise.all([loadJobs(), loadWorkflows()]).catch((err) => setError(err?.message || '短视频任务加载失败'));
|
Promise.all([loadJobs(), loadWorkflows(), loadStatus()]).catch((err) => setError(err?.message || '短视频任务加载失败'));
|
||||||
}, [loadJobs, loadWorkflows]);
|
}, [loadJobs, loadWorkflows, loadStatus]);
|
||||||
|
|
||||||
const selectedWorkflow = workflows.find((item) => item.publicId === templateId) || null;
|
const selectedWorkflow = workflows.find((item) => item.publicId === templateId) || null;
|
||||||
const useBuiltinCloudTemplate = videoBackend === 'comfy' && (!templateId || templateId === BUILTIN_CLOUD_TEMPLATE_VALUE);
|
const useBuiltinCloudTemplate = videoBackend === 'comfy' && isCloudComfy && templateId === BUILTIN_CLOUD_TEMPLATE_VALUE;
|
||||||
const canSubmit = Boolean(topic.trim()) && !submitting;
|
const comfyHasTemplate = useBuiltinCloudTemplate || Boolean(templateId && selectedWorkflow);
|
||||||
|
const canSubmit = Boolean(topic.trim()) && !submitting && (videoBackend !== 'comfy' || comfyHasTemplate);
|
||||||
|
const ratioOptions = useBuiltinCloudTemplate ? cloudRatios : DEFAULT_CLOUD_RATIOS;
|
||||||
|
const resolutionOptions = useBuiltinCloudTemplate ? cloudResolutions : DEFAULT_CLOUD_RESOLUTIONS;
|
||||||
|
const durationMin = useBuiltinCloudTemplate ? cloudDurationMin : 5;
|
||||||
|
const durationMax = useBuiltinCloudTemplate ? cloudDurationMax : 120;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (videoBackend !== 'comfy') return;
|
||||||
|
if (isCloudComfy) {
|
||||||
|
setTemplateId((current) => current || BUILTIN_CLOUD_TEMPLATE_VALUE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTemplateId((current) => current === BUILTIN_CLOUD_TEMPLATE_VALUE ? '' : current);
|
||||||
|
}, [isCloudComfy, videoBackend]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!useBuiltinCloudTemplate) return;
|
||||||
|
setDuration((current) => {
|
||||||
|
const numeric = Number.isFinite(current) ? current : cloudDefaultDuration;
|
||||||
|
return Math.max(cloudDurationMin, Math.min(cloudDurationMax, numeric));
|
||||||
|
});
|
||||||
|
setResolution((current) => cloudResolutions.includes(current) ? current : DEFAULT_BUILTIN_CLOUD_RESOLUTION);
|
||||||
|
setRatio((current) => cloudRatios.includes(current) ? current : '9:16');
|
||||||
|
}, [cloudDefaultDuration, cloudDurationMax, cloudDurationMin, cloudRatios, cloudResolutions, useBuiltinCloudTemplate]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (useBuiltinCloudTemplate) {
|
if (useBuiltinCloudTemplate) {
|
||||||
@@ -304,6 +359,16 @@ export default function MoneyPrinter() {
|
|||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
setError('');
|
setError('');
|
||||||
try {
|
try {
|
||||||
|
if (videoBackend === 'comfy' && !comfyHasTemplate) {
|
||||||
|
throw new Error(isCloudComfy ? '请选择 Cloud 内置模板或一个真实视频工作流模板' : '当前 Comfy 后端需要选择一个真实视频工作流模板');
|
||||||
|
}
|
||||||
|
const nextDuration = useBuiltinCloudTemplate
|
||||||
|
? Math.max(cloudDurationMin, Math.min(cloudDurationMax, Number(duration) || cloudDefaultDuration))
|
||||||
|
: duration;
|
||||||
|
const nextResolution = useBuiltinCloudTemplate && !cloudResolutions.includes(resolution)
|
||||||
|
? DEFAULT_BUILTIN_CLOUD_RESOLUTION
|
||||||
|
: resolution;
|
||||||
|
const nextRatio = useBuiltinCloudTemplate && !cloudRatios.includes(ratio) ? '9:16' : ratio;
|
||||||
const payload = await client.createMoneyPrinterJob({
|
const payload = await client.createMoneyPrinterJob({
|
||||||
topic: topic.trim(),
|
topic: topic.trim(),
|
||||||
scriptModel: scriptModel.trim(),
|
scriptModel: scriptModel.trim(),
|
||||||
@@ -311,9 +376,9 @@ export default function MoneyPrinter() {
|
|||||||
videoModel: videoModel.trim(),
|
videoModel: videoModel.trim(),
|
||||||
style,
|
style,
|
||||||
audience: audienceText,
|
audience: audienceText,
|
||||||
duration,
|
duration: nextDuration,
|
||||||
ratio,
|
ratio: nextRatio,
|
||||||
resolution,
|
resolution: nextResolution,
|
||||||
requirements,
|
requirements,
|
||||||
language: '中文',
|
language: '中文',
|
||||||
...(videoBackend === 'comfy' ? {
|
...(videoBackend === 'comfy' ? {
|
||||||
@@ -366,7 +431,7 @@ export default function MoneyPrinter() {
|
|||||||
<p className="page-subtitle">内置 MoneyPrinterTurbo 思路:主题生成脚本和分镜,再用 BoosAPI 视频模型生产成片。</p>
|
<p className="page-subtitle">内置 MoneyPrinterTurbo 思路:主题生成脚本和分镜,再用 BoosAPI 视频模型生产成片。</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="page-actions">
|
<div className="page-actions">
|
||||||
<button type="button" className="btn btn-ghost" onClick={() => void loadJobs()}>刷新</button>
|
<button type="button" className="btn btn-ghost" onClick={() => void refresh()}>刷新</button>
|
||||||
<button type="button" className="btn btn-primary" onClick={submit} disabled={!canSubmit}>
|
<button type="button" className="btn btn-primary" onClick={submit} disabled={!canSubmit}>
|
||||||
{submitting ? '提交中...' : '生成短视频'}
|
{submitting ? '提交中...' : '生成短视频'}
|
||||||
</button>
|
</button>
|
||||||
@@ -410,22 +475,18 @@ export default function MoneyPrinter() {
|
|||||||
</label>
|
</label>
|
||||||
<label className="money-printer-field">
|
<label className="money-printer-field">
|
||||||
<span>时长</span>
|
<span>时长</span>
|
||||||
<input type="number" min={5} max={120} value={duration} onChange={(event) => setDuration(Number(event.target.value || 30))} />
|
<input type="number" min={durationMin} max={durationMax} value={duration} onChange={(event) => setDuration(Number(event.target.value || durationMin))} />
|
||||||
</label>
|
</label>
|
||||||
<label className="money-printer-field">
|
<label className="money-printer-field">
|
||||||
<span>画幅</span>
|
<span>画幅</span>
|
||||||
<select value={ratio} onChange={(event) => setRatio(event.target.value)}>
|
<select value={ratio} onChange={(event) => setRatio(event.target.value)}>
|
||||||
<option value="9:16">9:16 竖屏</option>
|
{ratioOptions.map((item) => <option key={item} value={item}>{item}</option>)}
|
||||||
<option value="16:9">16:9 横屏</option>
|
|
||||||
<option value="1:1">1:1 方屏</option>
|
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<label className="money-printer-field">
|
<label className="money-printer-field">
|
||||||
<span>分辨率</span>
|
<span>分辨率</span>
|
||||||
<select value={resolution} onChange={(event) => setResolution(event.target.value)}>
|
<select value={resolution} onChange={(event) => setResolution(event.target.value)}>
|
||||||
<option value="1080p">1080p</option>
|
{resolutionOptions.map((item) => <option key={item} value={item}>{item}</option>)}
|
||||||
<option value="720p">720p</option>
|
|
||||||
<option value="480p">480p</option>
|
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -458,7 +519,8 @@ export default function MoneyPrinter() {
|
|||||||
<label className="money-printer-field">
|
<label className="money-printer-field">
|
||||||
<span>工作流模板</span>
|
<span>工作流模板</span>
|
||||||
<select value={templateId} onChange={(event) => setTemplateId(event.target.value)}>
|
<select value={templateId} onChange={(event) => setTemplateId(event.target.value)}>
|
||||||
<option value={BUILTIN_CLOUD_TEMPLATE_VALUE}>内置 Cloud Seedance 文生视频</option>
|
{isCloudComfy && <option value={BUILTIN_CLOUD_TEMPLATE_VALUE}>内置 Cloud Seedance 文生视频</option>}
|
||||||
|
{!isCloudComfy && <option value="">选择真实 Comfy 视频模板</option>}
|
||||||
{workflows.map((workflow) => (
|
{workflows.map((workflow) => (
|
||||||
<option key={workflow.publicId} value={workflow.publicId}>
|
<option key={workflow.publicId} value={workflow.publicId}>
|
||||||
{workflowLooksVideoCapable(workflow) ? '视频 · ' : '模板 · '}{workflowOptionLabel(workflow)}
|
{workflowLooksVideoCapable(workflow) ? '视频 · ' : '模板 · '}{workflowOptionLabel(workflow)}
|
||||||
@@ -469,7 +531,10 @@ export default function MoneyPrinter() {
|
|||||||
{useBuiltinCloudTemplate ? (
|
{useBuiltinCloudTemplate ? (
|
||||||
<div className="money-printer-template-meta">
|
<div className="money-printer-template-meta">
|
||||||
<strong>内置 Cloud Seedance 文生视频</strong>
|
<strong>内置 Cloud Seedance 文生视频</strong>
|
||||||
<span>无需导入模板,使用 Comfy Cloud 官方 ByteDanceTextToVideoNode。若账号无余额,会直接显示余额不足。</span>
|
<span>
|
||||||
|
当前后端为 Cloud,使用官方 {status?.builtinCloudTemplate?.nodeType || 'ByteDanceTextToVideoNode'}
|
||||||
|
{status?.builtinCloudTemplate?.model ? ` / ${status.builtinCloudTemplate.model}` : ''}。时长 {cloudDurationMin}-{cloudDurationMax} 秒,若账号无余额会直接显示余额不足。
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : selectedWorkflow ? (
|
) : selectedWorkflow ? (
|
||||||
<div className="money-printer-template-meta">
|
<div className="money-printer-template-meta">
|
||||||
@@ -480,7 +545,12 @@ export default function MoneyPrinter() {
|
|||||||
{mappingFromGraph(selectedWorkflow.graph) ? '已带节点映射' : '系统会按节点结构自动推断提示词和参数映射'}
|
{mappingFromGraph(selectedWorkflow.graph) ? '已带节点映射' : '系统会按节点结构自动推断提示词和参数映射'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : (
|
||||||
|
<div className="money-printer-template-meta is-warning">
|
||||||
|
<strong>需要视频工作流模板</strong>
|
||||||
|
<span>{isCloudComfy ? '可以使用内置 Cloud 模板,也可以选择模板库里的真实视频工作流。' : '当前不是 Cloud 后端,内置 Seedance 模板不可用,请选择 external/runninghub 可执行的视频模板。'}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-ghost money-printer-advanced-toggle"
|
className="btn btn-ghost money-printer-advanced-toggle"
|
||||||
|
|||||||
Reference in New Issue
Block a user