Merge branch 'main' of https://gitee.com/wg123/video-gen
This commit is contained in:
+548
File diff suppressed because one or more lines are too long
Vendored
+36
-36
@@ -1,37 +1,37 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<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.googleapis.com" />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<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" />
|
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
||||||
<title>后台管理</title>
|
<title>后台管理</title>
|
||||||
<script>
|
<script>
|
||||||
(function() {
|
(function() {
|
||||||
var cached = localStorage.getItem('siteInfo');
|
var cached = localStorage.getItem('siteInfo');
|
||||||
if (cached) {
|
if (cached) {
|
||||||
try {
|
try {
|
||||||
var info = JSON.parse(cached);
|
var info = JSON.parse(cached);
|
||||||
if (info.siteName) {
|
if (info.siteName) {
|
||||||
document.title = info.siteName + ' - 管理后台';
|
document.title = info.siteName + ' - 管理后台';
|
||||||
}
|
}
|
||||||
if (info.siteLogo) {
|
if (info.siteLogo) {
|
||||||
var link = document.querySelector('link[rel="icon"]');
|
var link = document.querySelector('link[rel="icon"]');
|
||||||
if (link) {
|
if (link) {
|
||||||
link.href = info.siteLogo;
|
link.href = info.siteLogo;
|
||||||
link.type = 'image/png';
|
link.type = 'image/png';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
<script type="module" crossorigin src="/assets/index-Yj3EUzSB.js"></script>
|
<script type="module" crossorigin src="/assets/index-ChXF-ot2.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-D7ShJUt4.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>␍
|
<div id="root"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ interface VideoEngine {
|
|||||||
supportedRatios: string[];
|
supportedRatios: string[];
|
||||||
supportedResolutions: string[];
|
supportedResolutions: string[];
|
||||||
supportedDurations: number[];
|
supportedDurations: number[];
|
||||||
|
maxDuration: number;
|
||||||
|
maxImageCount: number;
|
||||||
|
maxVideoCount: number;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
priority: number;
|
priority: number;
|
||||||
}
|
}
|
||||||
@@ -66,6 +69,9 @@ const AdminVideoEngines: React.FC = () => {
|
|||||||
supported_ratios: JSON.stringify(values.supportedRatios || []),
|
supported_ratios: JSON.stringify(values.supportedRatios || []),
|
||||||
supported_resolutions: JSON.stringify(values.supportedResolutions || []),
|
supported_resolutions: JSON.stringify(values.supportedResolutions || []),
|
||||||
supported_durations: JSON.stringify(values.supportedDurations || []),
|
supported_durations: JSON.stringify(values.supportedDurations || []),
|
||||||
|
max_duration: values.maxDuration ?? 15,
|
||||||
|
max_image_count: values.maxImageCount ?? 2,
|
||||||
|
max_video_count: values.maxVideoCount ?? 0,
|
||||||
is_active: values.isActive ?? true,
|
is_active: values.isActive ?? true,
|
||||||
priority: values.priority ?? 0,
|
priority: values.priority ?? 0,
|
||||||
};
|
};
|
||||||
@@ -102,11 +108,14 @@ const AdminVideoEngines: React.FC = () => {
|
|||||||
} else {
|
} else {
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
isActive: true, priority: 0,
|
isActive: true, priority: 0,
|
||||||
supportedRatios: ['16:9', '4:3', '1:1', '3:4', '9:16', '21:9'],
|
maxDuration: 15,
|
||||||
supportedResolutions: ['480p', '720p', '1080p'],
|
maxImageCount: 2,
|
||||||
supportedDurations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
maxVideoCount: 0,
|
||||||
});
|
supportedRatios: ['16:9', '4:3', '1:1', '3:4', '9:16', '21:9'],
|
||||||
|
supportedResolutions: ['480p', '720p', '1080p'],
|
||||||
|
supportedDurations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -142,6 +151,14 @@ const AdminVideoEngines: React.FC = () => {
|
|||||||
title: '支持时长', dataIndex: 'supportedDurations', width: 120,
|
title: '支持时长', dataIndex: 'supportedDurations', width: 120,
|
||||||
render: (d: number[]) => <Tag color="orange">{d?.length ? `${Math.min(...d)}-${Math.max(...d)}s` : '-'}</Tag>,
|
render: (d: number[]) => <Tag color="orange">{d?.length ? `${Math.min(...d)}-${Math.max(...d)}s` : '-'}</Tag>,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '最大图片', dataIndex: 'maxImageCount', width: 100,
|
||||||
|
render: (v: number) => <Tag color="purple">{v} 张</Tag>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '最大视频', dataIndex: 'maxVideoCount', width: 100,
|
||||||
|
render: (v: number) => <Tag color="cyan">{v} 个</Tag>,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '状态', dataIndex: 'isActive', width: 80,
|
title: '状态', dataIndex: 'isActive', width: 80,
|
||||||
render: (v: boolean) => <Tag color={v ? 'green' : 'default'}>{v ? '启用' : '停用'}</Tag>,
|
render: (v: boolean) => <Tag color={v ? 'green' : 'default'}>{v ? '启用' : '停用'}</Tag>,
|
||||||
@@ -234,6 +251,17 @@ const AdminVideoEngines: React.FC = () => {
|
|||||||
} />
|
} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</div>
|
</div>
|
||||||
|
<div style={{ display: 'flex', gap: 16 }}>
|
||||||
|
<Form.Item name="maxDuration" label="最大时长(秒)" style={{ flex: 1 }}>
|
||||||
|
<Input type="number" size="large" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="maxImageCount" label="最大图片数量" style={{ flex: 1 }}>
|
||||||
|
<Input type="number" size="large" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="maxVideoCount" label="最大视频数量" style={{ flex: 1 }}>
|
||||||
|
<Input type="number" size="large" />
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
<div style={{ display: 'flex', gap: 16 }}>
|
<div style={{ display: 'flex', gap: 16 }}>
|
||||||
<Form.Item name="priority" label="优先级">
|
<Form.Item name="priority" label="优先级">
|
||||||
<Select size="large" options={[
|
<Select size="large" options={[
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
"""add max image and video count to video engines
|
||||||
|
|
||||||
|
Revision ID: f7a3b2c1d4e5
|
||||||
|
Revises: e5f260ed1459
|
||||||
|
Create Date: 2026-07-01 00:00:00.000000
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
revision: str = 'f7a3b2c1d4e5'
|
||||||
|
down_revision: Union[str, None] = 'e5f260ed1459'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
op.add_column('video_engines', sa.Column('max_image_count', sa.Integer(), server_default='2', nullable=False))
|
||||||
|
op.add_column('video_engines', sa.Column('max_video_count', sa.Integer(), server_default='0', nullable=False))
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
op.drop_column('video_engines', 'max_video_count')
|
||||||
|
op.drop_column('video_engines', 'max_image_count')
|
||||||
@@ -48,40 +48,70 @@ async def get_credit_ratios(
|
|||||||
current_user: User = Depends(get_current_user),
|
current_user: User = Depends(get_current_user),
|
||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
):
|
):
|
||||||
async def get_ratios_for_engine_type(gen_type: str, engine_ids: list):
|
import json
|
||||||
for engine_id in engine_ids:
|
|
||||||
|
async def get_engine_with_ratios(gen_type: str, engines: list):
|
||||||
|
for engine in engines:
|
||||||
result = await db.execute(
|
result = await db.execute(
|
||||||
select(CreditRatio)
|
select(CreditRatio)
|
||||||
.where(CreditRatio.gen_type == gen_type)
|
.where(CreditRatio.gen_type == gen_type)
|
||||||
.where(CreditRatio.model_config_id == engine_id)
|
.where(CreditRatio.model_config_id == engine.id)
|
||||||
)
|
)
|
||||||
ratios = result.scalars().all()
|
ratios = result.scalars().all()
|
||||||
if ratios:
|
if ratios:
|
||||||
return [CreditRatioOut.model_validate(r) for r in ratios]
|
ratios_out = [CreditRatioOut.model_validate(r) for r in ratios]
|
||||||
return []
|
engine_info = {
|
||||||
|
"id": engine.id,
|
||||||
|
"name": engine.name,
|
||||||
|
"provider": engine.provider,
|
||||||
|
"ratios": ratios_out,
|
||||||
|
}
|
||||||
|
if gen_type == "video":
|
||||||
|
try:
|
||||||
|
supported_ratios = json.loads(engine.supported_ratios) if engine.supported_ratios else []
|
||||||
|
except Exception:
|
||||||
|
supported_ratios = []
|
||||||
|
try:
|
||||||
|
supported_resolutions = json.loads(engine.supported_resolutions) if engine.supported_resolutions else []
|
||||||
|
except Exception:
|
||||||
|
supported_resolutions = []
|
||||||
|
try:
|
||||||
|
supported_durations = json.loads(engine.supported_durations) if engine.supported_durations else []
|
||||||
|
except Exception:
|
||||||
|
supported_durations = []
|
||||||
|
engine_info.update({
|
||||||
|
"supported_ratios": supported_ratios,
|
||||||
|
"supported_resolutions": supported_resolutions,
|
||||||
|
"supported_durations": supported_durations,
|
||||||
|
"max_duration": engine.max_duration,
|
||||||
|
"max_image_count": engine.max_image_count,
|
||||||
|
"max_video_count": engine.max_video_count,
|
||||||
|
})
|
||||||
|
return engine_info
|
||||||
|
return None
|
||||||
|
|
||||||
video_engines_result = await db.execute(
|
video_engines_result = await db.execute(
|
||||||
select(VideoEngine.id)
|
select(VideoEngine)
|
||||||
.where(VideoEngine.is_active == True)
|
.where(VideoEngine.is_active == True)
|
||||||
.order_by(VideoEngine.priority.desc())
|
.order_by(VideoEngine.priority.desc())
|
||||||
)
|
)
|
||||||
video_engine_ids = video_engines_result.scalars().all()
|
video_engines = video_engines_result.scalars().all()
|
||||||
|
|
||||||
image_engines_result = await db.execute(
|
image_engines_result = await db.execute(
|
||||||
select(ImageEngine.id)
|
select(ImageEngine)
|
||||||
.where(ImageEngine.is_active == True)
|
.where(ImageEngine.is_active == True)
|
||||||
.order_by(ImageEngine.priority.desc())
|
.order_by(ImageEngine.priority.desc())
|
||||||
)
|
)
|
||||||
image_engine_ids = image_engines_result.scalars().all()
|
image_engines = image_engines_result.scalars().all()
|
||||||
|
|
||||||
grouped = {}
|
grouped = {}
|
||||||
|
|
||||||
video_ratios = await get_ratios_for_engine_type("video", video_engine_ids)
|
video_data = await get_engine_with_ratios("video", video_engines)
|
||||||
if video_ratios:
|
if video_data:
|
||||||
grouped["video"] = video_ratios
|
grouped["video"] = video_data
|
||||||
|
|
||||||
image_ratios = await get_ratios_for_engine_type("image", image_engine_ids)
|
image_data = await get_engine_with_ratios("image", image_engines)
|
||||||
if image_ratios:
|
if image_data:
|
||||||
grouped["image"] = image_ratios
|
grouped["image"] = image_data
|
||||||
|
|
||||||
return grouped
|
return grouped
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ async def sync_consumption(
|
|||||||
)
|
)
|
||||||
async def get_consumption_fields() -> Any | dict:
|
async def get_consumption_fields() -> Any | dict:
|
||||||
fields = [
|
fields = [
|
||||||
{"field": "id", "description": "主键"},
|
{"field": "id", "description": "编号"},
|
||||||
{"field": "advertiser_id", "description": "广告主id"},
|
{"field": "advertiser_id", "description": "广告主id"},
|
||||||
{"field": "material_id", "description": "素材id"},
|
{"field": "material_id", "description": "素材id"},
|
||||||
{"field": "consume_date", "description": "消耗日期"},
|
{"field": "consume_date", "description": "消耗日期"},
|
||||||
|
|||||||
@@ -49,5 +49,7 @@ async def list_active_engines(
|
|||||||
"supported_ratios": ratios,
|
"supported_ratios": ratios,
|
||||||
"supported_resolutions": resolutions,
|
"supported_resolutions": resolutions,
|
||||||
"supported_durations": durations,
|
"supported_durations": durations,
|
||||||
|
"max_image_count": e.max_image_count,
|
||||||
|
"max_video_count": e.max_video_count,
|
||||||
})
|
})
|
||||||
return {"items": items}
|
return {"items": items}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ class VideoEngine(Base, TimestampMixin):
|
|||||||
supported_resolutions: Mapped[str] = mapped_column(String(256), default='["480p","720p","1080p"]')
|
supported_resolutions: Mapped[str] = mapped_column(String(256), default='["480p","720p","1080p"]')
|
||||||
supported_durations: Mapped[str] = mapped_column(String(256), nullable=True, default='[4,5,6,7,8,9,10,11,12,13,14,15]')
|
supported_durations: Mapped[str] = mapped_column(String(256), nullable=True, default='[4,5,6,7,8,9,10,11,12,13,14,15]')
|
||||||
max_duration: Mapped[int] = mapped_column(Integer, default=15)
|
max_duration: Mapped[int] = mapped_column(Integer, default=15)
|
||||||
|
max_image_count: Mapped[int] = mapped_column(Integer, default=2)
|
||||||
|
max_video_count: Mapped[int] = mapped_column(Integer, default=0)
|
||||||
generate_url: Mapped[str | None] = mapped_column(String(512), nullable=True, default="")
|
generate_url: Mapped[str | None] = mapped_column(String(512), nullable=True, default="")
|
||||||
query_url: Mapped[str | None] = mapped_column(String(512), nullable=True, default="")
|
query_url: Mapped[str | None] = mapped_column(String(512), nullable=True, default="")
|
||||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ class VideoEngineCreate(BaseModel):
|
|||||||
supported_resolutions: str = Field(default='["480p","720p","1080p"]')
|
supported_resolutions: str = Field(default='["480p","720p","1080p"]')
|
||||||
supported_durations: str = Field(default='[4,5,6,7,8,9,10,11,12,13,14,15]')
|
supported_durations: str = Field(default='[4,5,6,7,8,9,10,11,12,13,14,15]')
|
||||||
max_duration: int = Field(default=15)
|
max_duration: int = Field(default=15)
|
||||||
|
max_image_count: int = Field(default=2)
|
||||||
|
max_video_count: int = Field(default=0)
|
||||||
generate_url: str = Field(default="", max_length=512)
|
generate_url: str = Field(default="", max_length=512)
|
||||||
query_url: str = Field(default="", max_length=512)
|
query_url: str = Field(default="", max_length=512)
|
||||||
is_active: bool = True
|
is_active: bool = True
|
||||||
@@ -33,6 +35,8 @@ class VideoEnginePublic(BaseModel):
|
|||||||
supported_ratios: list[str] = []
|
supported_ratios: list[str] = []
|
||||||
supported_resolutions: list[str] = []
|
supported_resolutions: list[str] = []
|
||||||
supported_durations: list[int] = []
|
supported_durations: list[int] = []
|
||||||
|
max_image_count: int = 2
|
||||||
|
max_video_count: int = 0
|
||||||
|
|
||||||
|
|
||||||
class VideoEngineListResponse(BaseModel):
|
class VideoEngineListResponse(BaseModel):
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ class UploadQueue:
|
|||||||
code = -1
|
code = -1
|
||||||
|
|
||||||
if code != 0:
|
if code != 0:
|
||||||
logger.error(f"Error getting account info: {json.dumps(account_info)}")
|
logger.error(f"获取账户信息失败: {json.dumps(account_info)}")
|
||||||
else:
|
else:
|
||||||
existing_account = await db.execute(
|
existing_account = await db.execute(
|
||||||
select(UserOAuthAccount).where(
|
select(UserOAuthAccount).where(
|
||||||
@@ -161,8 +161,7 @@ class UploadQueue:
|
|||||||
)
|
)
|
||||||
await db.commit()
|
await db.commit()
|
||||||
|
|
||||||
message = result.get('message') or result.get('error', 'Unknown error')
|
logger.info(f"上传任务{task_id}完成: {'success' if result.get('success') else 'failed'}. 上传结果: {json.dumps(result, ensure_ascii=False)}")
|
||||||
logger.info(f"Upload task {task_id} completed: {'success' if result.get('success') else 'failed'}. Message: {message}")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
async with async_session() as db:
|
async with async_session() as db:
|
||||||
@@ -323,6 +322,7 @@ async def _upload_to_juliang(
|
|||||||
response = await douyin_api.upload_image_material(oauth_id, data, files)
|
response = await douyin_api.upload_image_material(oauth_id, data, files)
|
||||||
|
|
||||||
if response["code"] != 0:
|
if response["code"] != 0:
|
||||||
|
logger.error(f"上传图片素材失败: {json.dumps(response, ensure_ascii=False)}")
|
||||||
return {
|
return {
|
||||||
"resource_id": resource.id,
|
"resource_id": resource.id,
|
||||||
"advertiser_id": advertiser_id,
|
"advertiser_id": advertiser_id,
|
||||||
@@ -409,6 +409,7 @@ async def _upload_to_juliang(
|
|||||||
response = await douyin_api.upload_video_material(oauth_id, data, files)
|
response = await douyin_api.upload_video_material(oauth_id, data, files)
|
||||||
|
|
||||||
if response["code"] != 0:
|
if response["code"] != 0:
|
||||||
|
logger.error(f"上传视频素材失败: {json.dumps(response, ensure_ascii=False)}")
|
||||||
return {
|
return {
|
||||||
"resource_id": resource.id,
|
"resource_id": resource.id,
|
||||||
"advertiser_id": advertiser_id,
|
"advertiser_id": advertiser_id,
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 897 KiB |
Reference in New Issue
Block a user