Merge branch 'main' of https://gitee.com/wg123/video-gen
This commit is contained in:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+36
-36
@@ -1,37 +1,37 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
||||
<title>后台管理</title>
|
||||
<script>
|
||||
(function() {
|
||||
var cached = localStorage.getItem('siteInfo');
|
||||
if (cached) {
|
||||
try {
|
||||
var info = JSON.parse(cached);
|
||||
if (info.siteName) {
|
||||
document.title = info.siteName + ' - 管理后台';
|
||||
}
|
||||
if (info.siteLogo) {
|
||||
var link = document.querySelector('link[rel="icon"]');
|
||||
if (link) {
|
||||
link.href = info.siteLogo;
|
||||
link.type = 'image/png';
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script type="module" crossorigin src="/assets/index-cvtLwU6k.js"></script>
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
||||
<title>后台管理</title>
|
||||
<script>
|
||||
(function() {
|
||||
var cached = localStorage.getItem('siteInfo');
|
||||
if (cached) {
|
||||
try {
|
||||
var info = JSON.parse(cached);
|
||||
if (info.siteName) {
|
||||
document.title = info.siteName + ' - 管理后台';
|
||||
}
|
||||
if (info.siteLogo) {
|
||||
var link = document.querySelector('link[rel="icon"]');
|
||||
if (link) {
|
||||
link.href = info.siteLogo;
|
||||
link.type = 'image/png';
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script type="module" crossorigin src="/assets/index-Bq0Cmo9c.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>␍
|
||||
</body>
|
||||
</html>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -25,6 +25,9 @@ interface CreditRatio {
|
||||
inputVideoRatio: number;
|
||||
inputVideoBaseCredits: number;
|
||||
inputVideoPerSecondCredits: number;
|
||||
inputImageRatio: number;
|
||||
inputImageBaseCredits: number;
|
||||
inputImagePerImageCredits: number;
|
||||
}
|
||||
|
||||
interface CreditRatioFormValues {
|
||||
@@ -37,6 +40,9 @@ interface CreditRatioFormValues {
|
||||
inputVideoRatio?: number;
|
||||
inputVideoBaseCredits?: number;
|
||||
inputVideoPerSecondCredits?: number;
|
||||
inputImageRatio?: number;
|
||||
inputImageBaseCredits?: number;
|
||||
inputImagePerImageCredits?: number;
|
||||
}
|
||||
|
||||
const DEFAULT_IMAGE_SIZES = ['2K', '4K'];
|
||||
@@ -136,6 +142,9 @@ const AdminCreditRatios: React.FC = () => {
|
||||
payload.input_video_base_credits = values.inputVideoBaseCredits ?? 0;
|
||||
payload.input_video_per_second_credits = values.inputVideoPerSecondCredits ?? 0;
|
||||
}
|
||||
payload.input_image_ratio = values.inputImageRatio ?? 1.0;
|
||||
payload.input_image_base_credits = values.inputImageBaseCredits ?? 0;
|
||||
payload.input_image_per_image_credits = values.inputImagePerImageCredits ?? 0;
|
||||
if (modal.ratio) {
|
||||
await saveCreditRatio({ id: modal.ratio.id, ...payload });
|
||||
message.success('已更新');
|
||||
@@ -188,6 +197,9 @@ const AdminCreditRatios: React.FC = () => {
|
||||
inputVideoRatio: ratio.inputVideoRatio,
|
||||
inputVideoBaseCredits: ratio.inputVideoBaseCredits,
|
||||
inputVideoPerSecondCredits: ratio.inputVideoPerSecondCredits,
|
||||
inputImageRatio: ratio.inputImageRatio,
|
||||
inputImageBaseCredits: ratio.inputImageBaseCredits,
|
||||
inputImagePerImageCredits: ratio.inputImagePerImageCredits,
|
||||
});
|
||||
} else {
|
||||
form.resetFields();
|
||||
@@ -199,6 +211,9 @@ const AdminCreditRatios: React.FC = () => {
|
||||
inputVideoRatio: 1.0,
|
||||
inputVideoBaseCredits: 0,
|
||||
inputVideoPerSecondCredits: 0.5,
|
||||
inputImageRatio: 1.0,
|
||||
inputImageBaseCredits: 0,
|
||||
inputImagePerImageCredits: 0.5,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -238,31 +253,60 @@ const AdminCreditRatios: React.FC = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '视频倍率', dataIndex: 'inputVideoRatio', width: 100,
|
||||
render: (v: number, r: CreditRatio) => (
|
||||
<Typography.Text>{r.genType === 'image' ? '-' : (
|
||||
<span style={{ color: v >= 2 ? '#ef4444' : v >= 1.5 ? '#f59e0b' : '#10b981' }}>x{v}</span>
|
||||
)}</Typography.Text>
|
||||
),
|
||||
title: '传入视频',
|
||||
children: [
|
||||
{
|
||||
title: '倍率', dataIndex: 'inputVideoRatio', width: 90,
|
||||
render: (v: number, r: CreditRatio) => (
|
||||
<Typography.Text>{r.genType === 'image' ? '-' : (
|
||||
<span style={{ color: v >= 2 ? '#ef4444' : v >= 1.5 ? '#f59e0b' : '#10b981' }}>x{v}</span>
|
||||
)}</Typography.Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '基础积分', dataIndex: 'inputVideoBaseCredits', width: 100,
|
||||
render: (v: number, r: CreditRatio) => (
|
||||
<Typography.Text>{r.genType === 'image' ? '-' : `${v} 积分`}</Typography.Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '每秒积分', dataIndex: 'inputVideoPerSecondCredits', width: 100,
|
||||
render: (v: number, r: CreditRatio) => (
|
||||
<Typography.Text>{r.genType === 'image' ? '-' : `${v} 积分/秒`}</Typography.Text>
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '视频基础积分', dataIndex: 'inputVideoBaseCredits', width: 110,
|
||||
render: (v: number, r: CreditRatio) => (
|
||||
<Typography.Text>{r.genType === 'image' ? '-' : `${v} 积分`}</Typography.Text>
|
||||
),
|
||||
title: '传入图片',
|
||||
children: [
|
||||
{
|
||||
title: '倍率', dataIndex: 'inputImageRatio', width: 90,
|
||||
render: (v: number) => (
|
||||
<span style={{ color: v >= 2 ? '#ef4444' : v >= 1.5 ? '#f59e0b' : '#10b981' }}>x{v}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '基础积分', dataIndex: 'inputImageBaseCredits', width: 100,
|
||||
render: (v: number) => <Typography.Text>{v} 积分</Typography.Text>,
|
||||
},
|
||||
{
|
||||
title: '每张积分', dataIndex: 'inputImagePerImageCredits', width: 100,
|
||||
render: (v: number) => <Typography.Text>{v} 积分/张</Typography.Text>,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '视频每秒积分', dataIndex: 'inputVideoPerSecondCredits', width: 110,
|
||||
render: (v: number, r: CreditRatio) => (
|
||||
<Typography.Text>{r.genType === 'image' ? '-' : `${v} 积分/秒`}</Typography.Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '示例计算(视频15秒,上传视频15秒)', key: 'example', width: 140,
|
||||
title: '示例计算', key: 'example', width: 160,
|
||||
render: (_: any, r: CreditRatio) => {
|
||||
let total: number;
|
||||
if (r.genType === 'image') {
|
||||
total = Math.round(r.baseCredits * r.ratio);
|
||||
if (r.inputImageRatio && r.inputImagePerImageCredits) {
|
||||
total += Math.round(
|
||||
(r.inputImageBaseCredits + r.inputImagePerImageCredits * 3) * r.inputImageRatio
|
||||
);
|
||||
}
|
||||
} else {
|
||||
total = Math.round((r.baseCredits + r.perSecondCredits * 15) * r.ratio);
|
||||
if (r.inputVideoRatio && r.inputVideoPerSecondCredits) {
|
||||
@@ -270,6 +314,11 @@ const AdminCreditRatios: React.FC = () => {
|
||||
(r.inputVideoBaseCredits + r.inputVideoPerSecondCredits * 15) * r.inputVideoRatio
|
||||
);
|
||||
}
|
||||
if (r.inputImageRatio && r.inputImagePerImageCredits) {
|
||||
total += Math.round(
|
||||
(r.inputImageBaseCredits + r.inputImagePerImageCredits * 3) * r.inputImageRatio
|
||||
);
|
||||
}
|
||||
}
|
||||
return <Typography.Text strong style={{ color: '#6366f1' }}>{total} 积分</Typography.Text>;
|
||||
},
|
||||
@@ -346,7 +395,7 @@ const AdminCreditRatios: React.FC = () => {
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
pagination={false}
|
||||
scroll={{ x: 1180 }}
|
||||
scroll={{ x: 1500 }}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
@@ -421,6 +470,29 @@ const AdminCreditRatios: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div style={{
|
||||
marginTop: 12,
|
||||
padding: '12px 16px',
|
||||
backgroundColor: '#f0fff4',
|
||||
borderRadius: 8,
|
||||
border: '1px solid #c6f6d5',
|
||||
}}>
|
||||
<Typography.Text strong style={{ display: 'block', marginBottom: 8, color: '#059669' }}>
|
||||
传入图片价格
|
||||
</Typography.Text>
|
||||
<div style={{ display: 'flex', gap: 16 }}>
|
||||
<Form.Item name="inputImageRatio" label="倍率" style={{ flex: 1, marginBottom: 0 }} rules={[{ required: true, message: '请输入传入图片倍率' }]}>
|
||||
<InputNumber min={0} max={10} step={0.1} style={{ width: '100%' }} size="large" />
|
||||
</Form.Item>
|
||||
<Form.Item name="inputImageBaseCredits" label="基础积分" style={{ flex: 1, marginBottom: 0 }} rules={[{ required: true, message: '请输入传入图片基础积分' }]}>
|
||||
<InputNumber min={0} max={500} style={{ width: '100%' }} size="large" />
|
||||
</Form.Item>
|
||||
<Form.Item name="inputImagePerImageCredits" label="每张积分" style={{ flex: 1, marginBottom: 0 }} rules={[{ required: true, message: '请输入传入图片每张积分' }]}>
|
||||
<InputNumber min={0} max={50} style={{ width: '100%' }} size="large" />
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
"""2026070901_add_credits_ratio_增加上传图片积分规则
|
||||
|
||||
Revision ID: 2026070901
|
||||
Revises: 6c1aaf036f43
|
||||
Create Date: 2026-07-01 00:00:00.000000
|
||||
|
||||
该文件包含 2026-07-01 的数据库迁移内容:
|
||||
1. 积分规则表增加传入视频计费字段(input_video_ratio, input_video_base_credits, input_video_per_second_credits)
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision: str = '2026070901'
|
||||
down_revision: Union[str, None] = '6c1aaf036f43'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ========================================
|
||||
# 2026-07-01 - 积分规则表增加传入图片计费字段
|
||||
# ========================================
|
||||
op.add_column('credit_ratios', sa.Column('input_image_ratio', sa.Float(), server_default='1.0', nullable=False))
|
||||
op.add_column('credit_ratios', sa.Column('input_image_base_credits', sa.Float(), server_default='0.0', nullable=False))
|
||||
op.add_column('credit_ratios', sa.Column('input_image_per_image_credits', sa.Float(), server_default='0.5', nullable=False))
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ========================================
|
||||
# 2026-07-01 - 积分规则表增加传入图片计费字段(回滚)
|
||||
# ========================================
|
||||
op.drop_column('credit_ratios', 'input_image_per_image_credits')
|
||||
op.drop_column('credit_ratios', 'input_image_base_credits')
|
||||
op.drop_column('credit_ratios', 'input_image_ratio')
|
||||
@@ -1383,6 +1383,7 @@ async def create_credit_ratio(
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
await db.commit()
|
||||
return ratio
|
||||
|
||||
|
||||
@@ -1422,6 +1423,7 @@ async def update_credit_ratio(
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
await db.commit()
|
||||
return ratio
|
||||
|
||||
|
||||
@@ -1455,6 +1457,7 @@ async def delete_credit_ratio(
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
await db.commit()
|
||||
return {"message": "ok"}
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ async def lifespan(app: FastAPI):
|
||||
os.makedirs(settings.UPLOAD_LOCAL_PATH, exist_ok=True)
|
||||
await init_database()
|
||||
await init_redis()
|
||||
await _seed_data()
|
||||
# await _seed_data()
|
||||
|
||||
# Start task queue (handles both video and image generation)
|
||||
from app.services.video_queue import task_queue
|
||||
|
||||
@@ -25,3 +25,6 @@ class CreditRatio(Base, TimestampMixin):
|
||||
input_video_ratio: Mapped[float] = mapped_column(Float, default=1.0)
|
||||
input_video_base_credits: Mapped[float] = mapped_column(Float, default=0.0)
|
||||
input_video_per_second_credits: Mapped[float] = mapped_column(Float, default=0.5)
|
||||
input_image_ratio: Mapped[float] = mapped_column(Float, default=1.0)
|
||||
input_image_base_credits: Mapped[float] = mapped_column(Float, default=0.0)
|
||||
input_image_per_image_credits: Mapped[float] = mapped_column(Float, default=0.0)
|
||||
|
||||
@@ -61,6 +61,24 @@ class CreditRatioCreate(BaseModel):
|
||||
description="传入视频每秒积分。视频生成时,用户上传参考视频每秒消耗的积分",
|
||||
examples=[0.5],
|
||||
)
|
||||
input_image_ratio: float = Field(
|
||||
default=1.0,
|
||||
ge=0,
|
||||
description="传入图片积分倍率。图片生成时,用户上传参考图片的额外积分倍率",
|
||||
examples=[1.0],
|
||||
)
|
||||
input_image_base_credits: float = Field(
|
||||
default=0.0,
|
||||
ge=0,
|
||||
description="传入图片基础积分。图片生成时,用户上传参考图片的基础积分",
|
||||
examples=[0.0],
|
||||
)
|
||||
input_image_per_image_credits: float = Field(
|
||||
default=0.0,
|
||||
ge=0,
|
||||
description="传入图片每张积分。图片生成时,用户上传参考图片每张消耗的积分",
|
||||
examples=[0.0],
|
||||
)
|
||||
|
||||
|
||||
class CreditRatioOut(CreditRatioCreate):
|
||||
|
||||
@@ -66,6 +66,7 @@ async def calc_video_credits(
|
||||
resolution: str,
|
||||
engine_id: str | None = None,
|
||||
input_video_duration: float | None = None,
|
||||
input_image_count: int | None = None,
|
||||
) -> float:
|
||||
"""Calculate video credits using CreditRatio table, with fallback to hardcoded.
|
||||
|
||||
@@ -75,6 +76,7 @@ async def calc_video_credits(
|
||||
3. 原硬编码默认算法。
|
||||
|
||||
input_video_duration: 用户上传的参考视频总时长(秒),不为空时额外计费
|
||||
input_image_count: 用户上传的参考图片数量,不为空时额外计费
|
||||
"""
|
||||
if not engine_id:
|
||||
video_engines_result = await db.execute(
|
||||
@@ -97,6 +99,11 @@ async def calc_video_credits(
|
||||
ratio.input_video_base_credits + ratio.input_video_per_second_credits * input_video_duration
|
||||
) * ratio.input_video_ratio
|
||||
base_cost += input_video_cost
|
||||
if input_image_count and input_image_count > 0:
|
||||
input_image_cost = (
|
||||
ratio.input_image_base_credits + ratio.input_image_per_image_credits * input_image_count
|
||||
) * ratio.input_image_ratio
|
||||
base_cost += input_image_cost
|
||||
return round(base_cost, 2)
|
||||
|
||||
base = 60.0
|
||||
@@ -105,6 +112,8 @@ async def calc_video_credits(
|
||||
total = (base + duration_cost) * multiplier
|
||||
if input_video_duration and input_video_duration > 0:
|
||||
total += input_video_duration * 0.5 * multiplier
|
||||
if input_image_count and input_image_count > 0:
|
||||
total += input_image_count * 0.5 * multiplier
|
||||
return round(total, 2)
|
||||
|
||||
|
||||
@@ -120,6 +129,7 @@ async def calc_image_credits(
|
||||
db: AsyncSession,
|
||||
image_size: str,
|
||||
engine_id: str | None = None,
|
||||
input_image_count: int | None = None,
|
||||
) -> float:
|
||||
"""Calculate image credits using CreditRatio table, with fallback to hardcoded.
|
||||
|
||||
@@ -127,6 +137,8 @@ async def calc_image_credits(
|
||||
1. gen_type=image + engine_id + image_size 精确规则;
|
||||
2. gen_type=image + image_size 下 base_credits/per_second_credits 最高规则;
|
||||
3. 原硬编码默认算法。
|
||||
|
||||
input_image_count: 用户上传的参考图片数量,不为空时额外计费
|
||||
"""
|
||||
# 如果engine_id为空,默认查询权重最高的图片引擎积分规则
|
||||
if not engine_id:
|
||||
@@ -144,12 +156,21 @@ async def calc_image_credits(
|
||||
engine_id=engine_id,
|
||||
)
|
||||
if ratio:
|
||||
return round(ratio.base_credits * ratio.ratio, 2)
|
||||
base_cost = ratio.base_credits * ratio.ratio
|
||||
if input_image_count and input_image_count > 0:
|
||||
input_image_cost = (
|
||||
ratio.input_image_base_credits + ratio.input_image_per_image_credits * input_image_count
|
||||
) * ratio.input_image_ratio
|
||||
base_cost += input_image_cost
|
||||
return round(base_cost, 2)
|
||||
|
||||
# Fallback
|
||||
multiplier = {"4K": 2.0, "2K": 1.0}.get(image_size, 1.0)
|
||||
base_cost = 4.0
|
||||
return round(base_cost * multiplier, 2)
|
||||
total = base_cost * multiplier
|
||||
if input_image_count and input_image_count > 0:
|
||||
total += input_image_count * 0.5 * multiplier
|
||||
return round(total, 2)
|
||||
|
||||
|
||||
async def _get_existing_credit_record_by_biz_key(
|
||||
|
||||
+3
-3
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -28,7 +28,7 @@
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script type="module" crossorigin src="/assets/index-CYMIldpb.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-DGh1eEOO.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-JhRVnnL-.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -382,30 +382,38 @@ const AIChatPage: React.FC = () => {
|
||||
config = {
|
||||
perSecondCredits: 2,
|
||||
baseCredits: 60,
|
||||
ratio: 1,
|
||||
inputVideoRatio: 1,
|
||||
ratio: 1.3,
|
||||
inputVideoRatio: 1.3,
|
||||
inputVideoBaseCredits: 0,
|
||||
inputVideoPerSecondCredits: 0.5,
|
||||
inputVideoPerSecondCredits: 15,
|
||||
inputImageRatio: 1,
|
||||
inputImageBaseCredits: 0,
|
||||
inputImagePerImageCredits: 0.0,
|
||||
};
|
||||
if (videoResolution === '1080p') {
|
||||
config.ratio = 2;
|
||||
config.ratio = 1.3;
|
||||
} else if (videoResolution === '720p') {
|
||||
config.ratio = 1.5;
|
||||
config.ratio = 1.3;
|
||||
} else if (videoResolution === '480p') {
|
||||
config.ratio = 1;
|
||||
config.ratio = 1.3;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 如果没有找到对应的配置,则使用默认配置
|
||||
config = {
|
||||
perSecondCredits: 0.1,
|
||||
baseCredits: 4,
|
||||
ratio: 1,
|
||||
baseCredits: 2,
|
||||
ratio: 3,
|
||||
inputImageRatio: 1,
|
||||
inputImageBaseCredits: 0,
|
||||
inputImagePerImageCredits: 0.0,
|
||||
};
|
||||
if (selectedResolution === '2K') {
|
||||
config.ratio = 1;
|
||||
config.ratio = 3;
|
||||
} else if (selectedResolution === '4K') {
|
||||
config.ratio = 2;
|
||||
config.ratio = 3;
|
||||
} else if (selectedResolution === '1K') {
|
||||
config.ratio = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -422,10 +430,27 @@ const AIChatPage: React.FC = () => {
|
||||
const inputVideoCost = ((config.inputVideoBaseCredits || 0) + (config.inputVideoPerSecondCredits || 0) * inputVideoDuration) * (config.inputVideoRatio || 1);
|
||||
total += inputVideoCost;
|
||||
}
|
||||
// 传入图片积分
|
||||
const inputImageCount = currentMedia
|
||||
.filter((m) => m.type === 'image')
|
||||
.length;
|
||||
if (inputImageCount > 0) {
|
||||
const inputImageCost = ((config.inputImageBaseCredits || 0) + (config.inputImagePerImageCredits || 0) * inputImageCount) * (config.inputImageRatio || 1);
|
||||
total += inputImageCost;
|
||||
}
|
||||
return Number(total.toFixed(2));
|
||||
} else {
|
||||
// 图片:baseCredits × ratio
|
||||
return Number((config.baseCredits * config.ratio).toFixed(2));
|
||||
let total = config.baseCredits * config.ratio;
|
||||
// 传入图片积分
|
||||
const inputImageCount = currentMedia
|
||||
.filter((m) => m.type === 'image')
|
||||
.length;
|
||||
if (inputImageCount > 0) {
|
||||
const inputImageCost = ((config.inputImageBaseCredits || 0) + (config.inputImagePerImageCredits || 0) * inputImageCount) * (config.inputImageRatio || 1);
|
||||
total += inputImageCost;
|
||||
}
|
||||
return Number(total.toFixed(2));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user