import axios from 'axios';
import * as crypto from 'crypto';
import path from 'path';
import fs from 'fs';
// @ts-ignore
import crc32 from 'crc32';
import { generate_a_bogus } from './utils/a_bogus.js';
import { generateMsToken, toUrlParams, generateUuid, jsonEncode, urlEncode, unixTimestamp } from './utils/index.js';
// 模型映射
const MODEL_MAP: Record<string, string> = {
'jimeng-3.1': 'high_aes_general_v30l_art_fangzhou:general_v3.0_18b',
'jimeng-3.0': 'high_aes_general_v30l:general_v3.0_18b',
'jimeng-2.1': 'high_aes_general_v21_L:general_v2.1_L',
'jimeng-2.0-pro': 'high_aes_general_v20_L:general_v2.0_L',
'jimeng-2.0': 'high_aes_general_v20:general_v2.0',
'jimeng-1.4': 'high_aes_general_v14:general_v1.4',
'jimeng-xl-pro': 'text2img_xl_sft',
// video
'jimeng-video-3.0-pro': 'dreamina_ic_generate_video_model_vgfm_3.0_pro',
'jimeng-video-3.0': 'dreamina_ic_generate_video_model_vgfm_3.0',
'jimeng-video-2.0': 'dreamina_ic_generate_video_model_vgfm_lite',
'jimeng-video-2.0-pro': 'dreamina_ic_generate_video_model_vgfm1.0'
};
// 常量定义
const DEFAULT_MODEL = 'jimeng-3.1';
const DEFAULT_VIDEO_MODEL = 'jimeng-video-3.0';
const DEFAULT_BLEND_MODEL = 'jimeng-3.0';
const DRAFT_VERSION = '3.0.2';
const DEFAULT_ASSISTANT_ID = '513695'; // 从原始仓库中提取
const WEB_ID = Math.random() * 999999999999999999 + 7000000000000000000;
const USER_ID = generateUuid().replace(/-/g, '');
const UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
// 接口定义
interface LogoInfo {
add_logo?: boolean; // 是否添加水印 默认不添加
position?: number; // 0-右下角 1-左下角 2-左上角 3-右上角
language?: number; // 0-中文(AI生成)1-英文(Generated by AI)
opacity?: number; // 0-1 default: 0.3
logo_text_content?: string; // 水印文字内容
}
interface ImageGenerationParams {
filePath?: string; // 图片路径
model?: string; // 模型名称,默认使用 DEFAULT_MODEL
prompt: string; // 提示词
width?: number; // 图像宽度,默认1024
height?: number; // 图像高度,默认1024
sample_strength?: number; // 精细度,默认0.5
negative_prompt?: string; // 反向提示词,默认空
refresh_token?: string; // 刷新令牌,必需
req_key?: string; // 自定义参数,兼容旧接口
}
interface VideoGenerationParams {
filePath?: string[]; // 首帧和尾帧路径,支持数组
resolution?: string; // 分辨率 720p 1080p
model?: string; // 模型名称,默认使用 DEFAULT_MODEL
prompt: string; // 提示词
width?: number; // 图像宽度,默认1024
height?: number; // 图像高度,默认1024
refresh_token?: string; // 刷新令牌,必需
req_key?: string; // 自定义参数,兼容旧接口
}
export function generateCookie(refreshToken: string) {
return [
`_tea_web_id=${WEB_ID}`,
`is_staff_user=false`,
`store-region=cn-gd`,
`store-region-src=uid`,
`sid_guard=${refreshToken}%7C${unixTimestamp()}%7C5184000%7CMon%2C+03-Feb-2025+08%3A17%3A09+GMT`,
`uid_tt=${USER_ID}`,
`uid_tt_ss=${USER_ID}`,
`sid_tt=${refreshToken}`,
`sessionid=${refreshToken}`,
`sessionid_ss=${refreshToken}`,
`sid_tt=${refreshToken}`
].join("; ");
}
// 即梦API客户端类
class JimengApiClient {
private refreshToken: string;
private getUploadImageProofUrl = 'https://imagex.bytedanceapi.com/'
constructor() {
this.refreshToken = process.env.JIMENG_API_TOKEN || '';
if (!this.refreshToken) {
throw new Error('JIMENG_API_TOKEN 环境变量未设置');
}
}
/**
* 获取模型映射
* @param model 模型名称
* @returns 映射后的模型名称
*/
private getModel(model: string): string {
return MODEL_MAP[model] || MODEL_MAP[DEFAULT_MODEL];
}
/**
* 发送请求到即梦API
* @param method 请求方法
* @param path 请求路径
* @param data 请求数据
* @param params 请求参数
* @param headers 请求头
* @returns 响应结果
*/
private async request(
method: string,
path: string,
data: any = {},
params: any = {},
headers: any = {}
): Promise<any> {
const baseUrl = 'https://jimeng.jianying.com';
const url = path.includes('https://') ? path : `${baseUrl}${path}`;
const FAKE_HEADERS = {
Accept: "application/json, text/plain, */*",
"Accept-Encoding": "gzip, deflate, br, zstd",
"Accept-language": "zh-CN,zh;q=0.9",
"Cache-control": "no-cache",
"Last-event-id": "undefined",
Appid: DEFAULT_ASSISTANT_ID,
Appvr: "5.8.0",
Origin: "https://jimeng.jianying.com",
Pragma: "no-cache",
Priority: "u=1, i",
Referer: "https://jimeng.jianying.com",
Pf: "7",
"Sec-Ch-Ua":
'"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
"Sec-Ch-Ua-Mobile": "?0",
"Sec-Ch-Ua-Platform": '"Windows"',
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"User-Agent": UA
};
const requestHeaders = {
...FAKE_HEADERS,
'Cookie': generateCookie(this.refreshToken),
...headers
};
try {
const response = await axios({
method: method.toLowerCase(),
url,
data: method.toUpperCase() !== 'GET' ? data : undefined,
params: method.toUpperCase() === 'GET' ? { ...data, ...params } : params,
headers: requestHeaders,
timeout: 60000
});
return response.data;
} catch (error) {
if (axios.isAxiosError(error) && error.response) {
throw new Error(`即梦API请求错误: ${JSON.stringify(error.response.data)}`);
} else {
throw new Error(`即梦API请求失败: ${error}`);
}
}
}
/**
* 获取积分信息
* @returns 积分信息
*/
public async getCredit(): Promise<Record<string, number>> {
const result = await this.request(
'POST',
'/commerce/v1/benefits/user_credit',
{},
{},
{ 'Referer': 'https://jimeng.jianying.com/ai-tool/image/generate' }
);
const credit = result.credit || {};
const giftCredit = credit.gift_credit || 0;
const purchaseCredit = credit.purchase_credit || 0;
const vipCredit = credit.vip_credit || 0;
return {
giftCredit,
purchaseCredit,
vipCredit,
totalCredit: giftCredit + purchaseCredit + vipCredit
};
}
/**
* 领取积分
*/
public async receiveCredit(): Promise<void> {
const credit = await this.request(
'POST',
'/commerce/v1/benefits/credit_receive',
{ 'time_zone': 'Asia/Shanghai' },
{},
{ 'Referer': 'https://jimeng.jianying.com/ai-tool/image/generate' }
);
console.log("领取积分", credit)
}
/**
* 即梦AI图像生成
* @param params 图像生成参数
* @returns 生成的图像URL列表
*/
public async generateImage(params: ImageGenerationParams): Promise<string[]> {
// 参数验证
if (!params.prompt || typeof params.prompt !== 'string') {
throw new Error('prompt必须是非空字符串');
}
const hasFilePath = params?.filePath
let uploadID = null
if (params?.filePath) {
uploadID = await this.uploadCoverFile(params.filePath)
}
// 获取实际模型
const modelName = hasFilePath ? DEFAULT_BLEND_MODEL : params.model || DEFAULT_MODEL;
const actualModel = this.getModel(modelName);
// 检查积分
const creditInfo = await this.getCredit();
if (creditInfo.totalCredit <= 0) {
await this.receiveCredit();
}
// 生成组件ID
const componentId = generateUuid();
const rqParams = {
"babi_param": urlEncode(jsonEncode({
"scenario": "image_video_generation",
"feature_key": hasFilePath ? "to_image_referenceimage_generate" : "aigc_to_image",
"feature_entrance": "to_image",
"feature_entrance_detail": hasFilePath ? "to_image-referenceimage-byte_edit" : `to_image-${actualModel}`,
})),
"aid": parseInt(DEFAULT_ASSISTANT_ID),
"device_platform": "web",
"region": "CN",
"web_id": WEB_ID
}
let abilities: Record<string, any> = {}
if (hasFilePath) {
abilities = {
"blend": {
"type": "",
"id": generateUuid(),
"min_features": [],
"core_param": {
"type": "",
"id": generateUuid(),
"model": actualModel,
"prompt": params.prompt + '##',
"sample_strength": params.sample_strength || 0.5,
"image_ratio": 1,
"large_image_info": {
"type": "",
"id": generateUuid(),
"height": 1360,
"width": 1360,
"resolution_type": '1k'
}
},
"ability_list": [
{
"type": "",
"id": generateUuid(),
"name": "byte_edit",
"image_uri_list": [
uploadID
],
"image_list": [
{
"type": "image",
"id": generateUuid(),
"source_from": "upload",
"platform_type": 1,
"name": "",
"image_uri": uploadID,
"width": 0,
"height": 0,
"format": "",
"uri": uploadID
}
],
"strength": 0.5
}
],
"history_option": {
"type": "",
"id": generateUuid(),
},
"prompt_placeholder_info_list": [
{
"type": "",
"id": generateUuid(),
"ability_index": 0
}
],
"postedit_param": {
"type": "",
"id": generateUuid(),
"generate_type": 0
}
}
}
} else {
abilities = {
"generate": {
"type": "",
"id": generateUuid(),
"core_param": {
"type": "",
"id": generateUuid(),
"model": actualModel,
"prompt": params.prompt,
"negative_prompt": params.negative_prompt || "",
"seed": Math.floor(Math.random() * 100000000) + 2500000000,
"sample_strength": params.sample_strength || 0.5,
"image_ratio": 1,
"large_image_info": {
"type": "",
"id": generateUuid(),
"height": params.height || 1024,
"width": params.width || 1024,
"resolution_type": '1k'
}
},
"history_option": {
"type": "",
"id": generateUuid(),
}
}
}
}
const rqData = {
"extend": {
"root_model": actualModel,
"template_id": "",
},
"submit_id": generateUuid(),
"metrics_extra": hasFilePath ? undefined : jsonEncode({
"templateId": "",
"generateCount": 1,
"promptSource": "custom",
"templateSource": "",
"lastRequestId": "",
"originRequestId": "",
}),
"draft_content": jsonEncode({
"type": "draft",
"id": generateUuid(),
"min_version": DRAFT_VERSION,
"is_from_tsn": true,
"version": "3.2.2",
"main_component_id": componentId,
"component_list": [{
"type": "image_base_component",
"id": componentId,
"min_version": DRAFT_VERSION,
"metadata": {
"type": "",
"id": generateUuid(),
"created_platform": 3,
"created_platform_version": "",
"created_time_in_ms": Date.now(),
"created_did": ""
},
"generate_type": hasFilePath ? "blend" : "generate",
"aigc_mode": "workbench",
"abilities": {
"type": "",
"id": generateUuid(),
...abilities
}
}]
}),
}
// 发送生成请求
const result = await this.request(
'POST',
'/mweb/v1/aigc_draft/generate',
rqData,
rqParams
);
const itemList = await this.pollResultWithHistory(result);
// 提取图片URL
const resultList = (itemList || []).map(item => {
const imageUrl = item?.image?.large_images?.[0]?.image_url || item?.common_attr?.cover_url;
return imageUrl;
}).filter(Boolean)
console.log('生成图片结果:', resultList)
return resultList
}
async pollResultWithHistory(result: any): Promise<any[]> {
// 获取历史记录ID
const historyId = result?.data?.aigc_data?.history_record_id;
if (!historyId) {
if (result?.errmsg) {
throw new Error(result.errmsg);
} else {
throw new Error('记录ID不存在');
}
}
// 轮询获取结果
let status = 20;
let failCode = null;
let itemList: any[] = [];
while (status === 20) {
await new Promise(resolve => setTimeout(resolve, 2000));
const result = await this.request(
'POST',
'/mweb/v1/get_history_by_ids',
{
"history_ids": [historyId],
"image_info": {
"width": 2048,
"height": 2048,
"format": "webp",
"image_scene_list": [
{ "scene": "smart_crop", "width": 360, "height": 360, "uniq_key": "smart_crop-w:360-h:360", "format": "webp" },
{ "scene": "smart_crop", "width": 480, "height": 480, "uniq_key": "smart_crop-w:480-h:480", "format": "webp" },
{ "scene": "smart_crop", "width": 720, "height": 720, "uniq_key": "smart_crop-w:720-h:720", "format": "webp" },
{ "scene": "smart_crop", "width": 720, "height": 480, "uniq_key": "smart_crop-w:720-h:480", "format": "webp" },
{ "scene": "smart_crop", "width": 360, "height": 240, "uniq_key": "smart_crop-w:360-h:240", "format": "webp" },
{ "scene": "smart_crop", "width": 240, "height": 320, "uniq_key": "smart_crop-w:240-h:320", "format": "webp" },
{ "scene": "smart_crop", "width": 480, "height": 640, "uniq_key": "smart_crop-w:480-h:640", "format": "webp" },
{ "scene": "normal", "width": 2400, "height": 2400, "uniq_key": "2400", "format": "webp" },
{ "scene": "normal", "width": 1080, "height": 1080, "uniq_key": "1080", "format": "webp" },
{ "scene": "normal", "width": 720, "height": 720, "uniq_key": "720", "format": "webp" },
{ "scene": "normal", "width": 480, "height": 480, "uniq_key": "480", "format": "webp" },
{ "scene": "normal", "width": 360, "height": 360, "uniq_key": "360", "format": "webp" }
]
},
"http_common_info": {
"aid": parseInt(DEFAULT_ASSISTANT_ID)
}
}
);
const record = result?.data?.[historyId];
if (!record) {
throw new Error('记录不存在');
}
status = record.status;
failCode = record.fail_code;
if (status === 30) {
if (failCode === '2038') {
throw new Error('内容被过滤');
}
throw new Error('生成失败');
}
if (record.item_list && record.item_list.length > 0) {
return record.item_list as any[];
}
}
return []
}
/**
* 获取上传凭证所需Ak和Tk
*/
private async getUploadAuth(): Promise<any> {
return new Promise(async (resolve, reject) => {
try {
const authRes = await this.request(
'POST',
'/mweb/v1/get_upload_token?aid=513695&da_version=3.2.2&aigc_features=app_lip_sync',
{
scene: 2
},
{},
);
if (
!authRes.data
) {
reject(authRes.errmsg ?? '获取上传凭证失败,账号可能已掉线!');
return;
}
resolve(authRes.data);
} catch (err) {
console.error('获取上传凭证失败:', err);
reject(err);
}
});
}
public async getFileContent(filePath: string): Promise<Buffer> {
try {
if (filePath.includes('https://') || filePath.includes('http://')) {
// 直接用axios获取图片Buffer
const res = await axios.get(filePath, { responseType: 'arraybuffer' });
return Buffer.from(res.data);
} else {
// 确保路径是绝对路径
const absolutePath = path.resolve(filePath);
// 读取文件内容
return await fs.promises.readFile(absolutePath);
}
} catch (error) {
console.error('Failed to read file:', error);
throw new Error(`读取文件失败: filePath`);
}
}
private generateRandomString(length: number): string {
let result = '';
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
/**
* 生成请求所需Header
*/
private addHeaders(
amzDate: string,
sessionToken: string,
requestBody: any,
): any {
const headers = {
'X-Amz-Date': amzDate,
'X-Amz-Security-Token': sessionToken,
};
if (Object.keys(requestBody).length > 0) {
// @ts-ignore
headers['X-Amz-Content-Sha256'] = crypto
.createHash('sha256')
.update(JSON.stringify(requestBody))
.digest('hex');
}
return headers;
}
/**
* 生成请求所需Header
*/
private async generateAuthorizationAndHeader(
accessKeyID: string,
secretAccessKey: string,
sessionToken: string,
region: string,
service: string,
requestMethod: string,
requestParams: any,
requestBody: any = {},
): Promise<any> {
return new Promise((resolve) => {
// 获取当前ISO时间
const now = new Date();
const amzDate = now.toISOString().replace(/[:\-]|\.\d{3}/g, '').slice(0, 15) + 'Z';
// 生成请求的Header
const requestHeaders: Record<string, string> = this.addHeaders(
amzDate,
sessionToken,
requestBody,
)
if (Object.keys(requestBody).length > 0) {
// @ts-ignore
requestHeaders['X-Amz-Content-Sha256'] = crypto
.createHash('sha256')
.update(JSON.stringify(requestBody))
.digest('hex')
}
// 生成请求的Authorization
const authorizationParams = [
'AWS4-HMAC-SHA256 Credential=' + accessKeyID + '/' +
this.credentialString(amzDate, region, service),
'SignedHeaders=' + this.signedHeaders(requestHeaders),
'Signature=' + this.signature(
secretAccessKey,
amzDate,
region,
service,
requestMethod,
requestParams,
requestHeaders,
requestBody,
),
];
const authorization = authorizationParams.join(', ');
// 返回Headers
const headers: any = {};
for (const key in requestHeaders) {
headers[key] = requestHeaders[key];
}
headers['Authorization'] = authorization;
resolve(headers);
});
}
/**
* 获取credentialString
*/
private credentialString(
amzDate: string,
region: string,
service: string,
): string {
const credentialArr = [
amzDate.substring(0, 8),
region,
service,
'aws4_request',
];
return credentialArr.join('/');
}
/**
* 生成http请求参数字符串
*/
private httpBuildQuery(params: any): string {
const searchParams = new URLSearchParams();
for (const key in params) {
if (params?.hasOwnProperty(key)) {
searchParams.append(key, params[key]);
}
}
return searchParams.toString();
}
private signedHeaders(requestHeaders: any): string {
const headers: string[] = [];
Object.keys(requestHeaders).forEach(function (r) {
r = r.toLowerCase();
headers.push(r);
});
return headers.sort().join(';');
}
/**
* 生成canonicalString
*/
private canonicalString(
requestMethod: string,
requestParams: any,
requestHeaders: any,
requestBody: any,
): string {
let canonicalHeaders: string[] = [];
const headerKeys = Object.keys(requestHeaders).sort();
for (let i = 0; i < headerKeys.length; i++) {
canonicalHeaders.push(
headerKeys[i].toLowerCase() + ':' + requestHeaders[headerKeys[i]],
);
}
// @ts-ignore
canonicalHeaders = canonicalHeaders.join('\n') + '\n';
let body = '';
if (Object.keys(requestBody).length > 0) {
body = JSON.stringify(requestBody);
}
const canonicalStringArr = [
requestMethod.toUpperCase(),
'/',
this.httpBuildQuery(requestParams),
canonicalHeaders,
this.signedHeaders(requestHeaders),
crypto.createHash('sha256').update(body).digest('hex'),
];
return canonicalStringArr.join('\n');
}
private signature(
secretAccessKey: string,
amzDate: string,
region: string,
service: string,
requestMethod: string,
requestParams: any,
requestHeaders: any,
requestBody: any,
): string {
// 生成signingKey
const amzDay = amzDate.substring(0, 8);
const kDate = crypto
.createHmac('sha256', 'AWS4' + secretAccessKey)
.update(amzDay)
.digest();
const kRegion = crypto.createHmac('sha256', kDate).update(region).digest();
const kService = crypto
.createHmac('sha256', kRegion)
.update(service)
.digest();
const signingKey = crypto
.createHmac('sha256', kService)
.update('aws4_request')
.digest();
// 生成StringToSign
const stringToSignArr = [
'AWS4-HMAC-SHA256',
amzDate,
this.credentialString(amzDate, region, service),
crypto
.createHash('sha256')
.update(
this.canonicalString(
requestMethod,
requestParams,
requestHeaders,
requestBody,
),
)
.digest('hex'),
];
const stringToSign = stringToSignArr.join('\n');
return crypto
.createHmac('sha256', signingKey)
.update(stringToSign)
.digest('hex');
}
/**
* 上传文件到远程服务器
* @param url 上传地址
* @param fileContent 文件内容
* @param headers 请求头
* @param method HTTP 方法
* @param proxy
*/
private async uploadFile(
url: string,
fileContent: Buffer,
headers: any,
method: string = 'PUT',
): Promise<any> {
return new Promise(async (resolve, reject) => {
const res = await this.request(
'POST',
url,
fileContent,
{},
headers
);
resolve(res);
});
}
/**
* 上传文件
*/
private async uploadCoverFile(
filePath: string,
): Promise<string> {
return new Promise(async (resolve, reject) => {
try {
console.log('开始上传文件:', filePath);
// 获取上传令牌所需Ak和Tk
const uploadAuth = await this.getUploadAuth();
// 获取图片数据
const imageRes = await this.getFileContent(filePath);
// 获取图片Crc32标识
const imageCrc32 = crc32(imageRes).toString(16);
// 获取图片上传凭证签名所需参数
const getUploadImageProofRequestParams = {
Action: 'ApplyImageUpload',
FileSize: imageRes.length,
ServiceId: 'tb4s082cfz',
Version: '2018-08-01',
s: this.generateRandomString(11),
};
// 获取图片上传请求头
const requestHeadersInfo = await this.generateAuthorizationAndHeader(
uploadAuth.access_key_id,
uploadAuth.secret_access_key,
uploadAuth.session_token,
'cn-north-1',
'imagex',
'GET',
getUploadImageProofRequestParams,
);
// 获取图片上传凭证
const uploadImgRes = await this.request(
'GET',
this.getUploadImageProofUrl + '?' +
this.httpBuildQuery(getUploadImageProofRequestParams),
{},
{},
requestHeadersInfo
);
if (uploadImgRes?.['Response ']?.hasOwnProperty('Error')) {
reject(uploadImgRes['Response ']['Error']['Message']);
return;
}
const UploadAddress = uploadImgRes.Result.UploadAddress;
// 用凭证拼接上传图片接口
const uploadImgUrl = `https://${UploadAddress.UploadHosts[0]}/upload/v1/${UploadAddress.StoreInfos[0].StoreUri}`;
// 上传图片
const imageUploadRes = await this.uploadFile(
uploadImgUrl,
imageRes,
{
Authorization: UploadAddress.StoreInfos[0].Auth,
'Content-Crc32': imageCrc32,
'Content-Type': 'application/octet-stream',
// 'X-Storage-U': '3674996648187204',
},
'POST',
);
if (imageUploadRes.code !== 2000) {
reject(imageUploadRes.message);
return;
}
const commitImgParams = {
Action: 'CommitImageUpload',
FileSize: imageRes.length,
ServiceId: 'tb4s082cfz',
Version: '2018-08-01',
// user_id: userUid,
};
const commitImgContent = {
SessionKey: UploadAddress.SessionKey,
};
const commitImgHead = await this.generateAuthorizationAndHeader(
uploadAuth.access_key_id,
uploadAuth.secret_access_key,
uploadAuth.session_token,
'cn-north-1',
'imagex',
'POST',
commitImgParams,
commitImgContent,
);
// 提交图片上传
const commitImg = await this.request(
'POST',
this.getUploadImageProofUrl +
'?' +
this.httpBuildQuery(commitImgParams),
commitImgContent,
{},
{
...commitImgHead,
'Content-Type': 'application/json',
}
);
if (commitImg['Response ']?.hasOwnProperty('Error')) {
reject(commitImg['Response ']['Error']['Message']);
return;
}
resolve(commitImg.Result.Results[0].Uri);
} catch (err: any) {
console.error('上传文件失败:', err);
const errorMessage = err?.message || err || '未知';
reject('上传失败,失败原因:' + errorMessage);
}
});
}
async generateVideo(params: VideoGenerationParams): Promise<string> {
if (!params.prompt || typeof params.prompt !== 'string') {
throw new Error('prompt必须是非空字符串');
}
const actualModel = this.getModel(params.model || DEFAULT_VIDEO_MODEL);
// 检查积分
const creditInfo = await this.getCredit();
if (creditInfo.totalCredit <= 0) {
await this.receiveCredit();
}
let first_frame_image = undefined
let end_frame_image = undefined
if (params?.filePath) {
let uploadIDs: any[] = []
for (const item of params.filePath) {
const uploadID = await this.uploadCoverFile(item)
uploadIDs.push(uploadID)
}
if (uploadIDs[0]) {
first_frame_image = {
format: "",
height: params.height || 1024,
id: generateUuid(),
image_uri: uploadIDs[0],
name: "",
platform_type: 1,
source_from: "upload",
type: "image",
uri: uploadIDs[0],
width: params.width || 1024,
}
}
if (uploadIDs[1]) {
end_frame_image = {
format: "",
height: params.height || 1024,
id: generateUuid(),
image_uri: uploadIDs[1],
name: "",
platform_type: 1,
source_from: "upload",
type: "image",
uri: uploadIDs[1],
width: params.width || 1024,
}
}
if (!first_frame_image && !end_frame_image) {
throw new Error('上传封面图片失败,请检查图片路径是否正确');
}
}
const componentId = generateUuid();
const metricsExtra = jsonEncode({
"enterFrom": "click",
"isDefaultSeed": 1,
"promptSource": "custom",
"isRegenerate": false,
"originSubmitId": generateUuid(),
})
const rqParams: {
[key: string]: string | number
} = {
msToken: generateMsToken(),
aigc_features: "app_lip_sync",
web_version: "6.6.0",
"da_version": "3.2.8",
"aid": parseInt(DEFAULT_ASSISTANT_ID),
"device_platform": "web",
"region": "CN",
"web_id": WEB_ID
}
rqParams['a_bogus'] = generate_a_bogus(toUrlParams(rqParams), UA)
const rqData = {
"extend": {
"root_model": end_frame_image ? MODEL_MAP['jimeng-video-3.0'] : actualModel,
"m_video_commerce_info": {
benefit_type: "basic_video_operation_vgfm_v_three",
resource_id: "generate_video",
resource_id_type: "str",
resource_sub_type: "aigc"
},
"m_video_commerce_info_list": [{
benefit_type: "basic_video_operation_vgfm_v_three",
resource_id: "generate_video",
resource_id_type: "str",
resource_sub_type: "aigc"
}]
},
"submit_id": generateUuid(),
"metrics_extra": metricsExtra,
"draft_content": jsonEncode({
"type": "draft",
"id": generateUuid(),
"min_version": "3.0.5",
"is_from_tsn": true,
"version": "3.2.8",
"main_component_id": componentId,
"component_list": [{
"type": "video_base_component",
"id": componentId,
"min_version": "1.0.0",
"metadata": {
"type": "",
"id": generateUuid(),
"created_platform": 3,
"created_platform_version": "",
"created_time_in_ms": Date.now(),
"created_did": ""
},
"generate_type": "gen_video",
"aigc_mode": "workbench",
"abilities": {
"type": "",
"id": generateUuid(),
"gen_video": {
"id": generateUuid(),
"type": "",
"text_to_video_params": {
"type": "",
"id": generateUuid(),
"model_req_key": actualModel,
"priority": 0,
"seed": Math.floor(Math.random() * 100000000) + 2500000000,
"video_aspect_ratio": "1:1",
"video_gen_inputs": [{
duration_ms: 5000,
first_frame_image: first_frame_image,
end_frame_image: end_frame_image,
fps: 24,
id: generateUuid(),
min_version: "3.0.5",
prompt: params.prompt,
resolution: params.resolution || "720p",
type: "",
video_mode: 2
}]
},
"video_task_extra": metricsExtra,
}
}
}],
}),
}
// 发送生成请求
const result = await this.request(
'POST',
'/mweb/v1/aigc_draft/generate',
rqData,
rqParams
);
const itemList = await this.pollResultWithHistory(result);
const videoUrl = itemList?.[0]?.video?.transcoded_video?.origin?.video_url
console.log('生成视频结果:', videoUrl);
return videoUrl;
}
}
// 创建API客户端实例
const apiClient = new JimengApiClient();
// 导出函数,保持对外接口不变
export const generateImage = (params: ImageGenerationParams): Promise<string[]> => {
return apiClient.generateImage(params);
};
export const generateVideo = (params: VideoGenerationParams): Promise<string> => {
return apiClient.generateVideo(params)
}
// 导出接口定义,以便其他模块使用
export type { ImageGenerationParams, LogoInfo };