641 lines
23 KiB
TypeScript
641 lines
23 KiB
TypeScript
import axios from 'axios'
|
||
import { MJBasic } from './mjBasic'
|
||
import { ImageGenerateMode, MJRobotType, MJSpeed } from '@/define/data/mjData'
|
||
import { MJRespoonseType } from '@/define/enum/mjEnum'
|
||
import { GetApiDefineDataById } from '@/define/data/apiData'
|
||
import { isEmpty } from 'lodash'
|
||
import { BookBackTaskStatus } from '@/define/enum/bookEnum'
|
||
import { MJ } from '@/define/model/mj'
|
||
import { define } from '@/define/define'
|
||
|
||
/**
|
||
* MidJourney API 账户过滤器接口
|
||
*
|
||
* 该接口定义了与MidJourney API请求中账户过滤相关的配置选项。
|
||
* 用于在API请求中指定任务处理速度、备注信息和实例ID等账户级别的设置。
|
||
*
|
||
* @interface AccountFilter
|
||
* @property {['FAST' | 'RELAX']?} modes - 处理模式数组,可选值为'FAST'(快速)或'RELAX'(普通)
|
||
* @property {string?} remark - 可选的备注信息,通常用于标识请求来源
|
||
* @property {string?} instanceId - 可选的实例ID,用于特定场景下的实例标识
|
||
*
|
||
* @example
|
||
* // 创建一个账户过滤器对象
|
||
* const filter: AccountFilter = {
|
||
* modes: ['FAST'],
|
||
* remark: '请求来源标识',
|
||
* instanceId: ''
|
||
* };
|
||
*/
|
||
interface AccountFilter {
|
||
modes?: ['FAST' | 'RELAX']
|
||
remark?: string // 添加问号使其成为可选属性
|
||
instanceId?: string // 添加问号使其成为可选属性
|
||
}
|
||
|
||
/**
|
||
* MidJourney API 图像生成请求体接口
|
||
*
|
||
* 该接口定义了向MidJourney API提交图像生成请求时所需的请求体结构。
|
||
* 包含指定的机器人类型、提示词文本和可选的账户过滤器设置。
|
||
*
|
||
* @interface MJAPIImagineRequestBody
|
||
* @property {'MID_JOURNEY' | 'NIJI_JOURNEY'} botType - 使用的机器人类型,MidJourney或NijiJourney
|
||
* @property {string} prompt - 用于生成图像的提示词文本
|
||
* @property {AccountFilter} [accountFilter] - 可选的账户过滤设置,用于指定处理速度等参数
|
||
*
|
||
* @example
|
||
* // 创建一个图像生成请求体
|
||
* const requestBody: MJAPIImagineRequestBody = {
|
||
* botType: 'MID_JOURNEY',
|
||
* prompt: 'a beautiful sunset over mountains, photorealistic style',
|
||
* accountFilter: {
|
||
* modes: ['FAST']
|
||
* }
|
||
* };
|
||
*/
|
||
interface MJAPIImagineRequestBody {
|
||
botType: 'MID_JOURNEY' | 'NIJI_JOURNEY'
|
||
prompt: string
|
||
accountFilter?: AccountFilter
|
||
}
|
||
|
||
/**
|
||
* MidJourney API 图像描述请求体接口
|
||
*
|
||
* 该接口定义了向MidJourney API提交图像描述(反推)请求时所需的请求体结构。
|
||
* 包含指定的机器人类型、base64编码的图像数据和可选的账户过滤器设置。
|
||
*
|
||
* @interface MJAPIDescribeRequestBody
|
||
* @property {'MID_JOURNEY' | 'NIJI_JOURNEY'} botType - 使用的机器人类型,MidJourney或NijiJourney
|
||
* @property {string} base64 - base64编码的图像数据字符串,用于图像描述/反推
|
||
* @property {AccountFilter} [accountFilter] - 可选的账户过滤设置,用于指定处理速度等参数
|
||
*
|
||
* @example
|
||
* // 创建一个图像描述请求体
|
||
* const requestBody: MJAPIDescribeRequestBody = {
|
||
* botType: 'MID_JOURNEY',
|
||
* base64: 'data:image/png;base64,iVBORw0KGgo...',
|
||
* accountFilter: {
|
||
* modes: ['FAST']
|
||
* }
|
||
* };
|
||
*/
|
||
interface MJAPIDescribeRequestBody {
|
||
botType: 'MID_JOURNEY' | 'NIJI_JOURNEY'
|
||
base64: string
|
||
accountFilter?: AccountFilter
|
||
}
|
||
|
||
/**
|
||
* MidJourney API 服务类
|
||
*
|
||
* 该类负责处理与MidJourney API的所有交互,包括图像生成和图像描述(反推提示词)功能。
|
||
* 扩展自MJBasic基类,继承了基本设置和配置管理功能。类提供了对MJ API的完整封装,
|
||
* 包括初始化配置、构建请求、发送API请求、处理响应和错误处理等。
|
||
*
|
||
* 主要功能:
|
||
* - 初始化MidJourney API设置和配置
|
||
* - 提交图像生成(imagine)请求
|
||
* - 提交图像描述(describe/反推)请求
|
||
* - 查询任务状态和获取结果
|
||
* - 构建符合API要求的请求体
|
||
* - 处理API响应和错误
|
||
*
|
||
* 使用场景:
|
||
* - 小说分镜的AI图像生成
|
||
* - 根据已有图像获取描述文本(反推提示词)
|
||
* - 监控和获取长时间运行任务的状态
|
||
*
|
||
* @class MJApiService
|
||
* @extends MJBasic
|
||
*
|
||
* @example
|
||
* // 初始化服务
|
||
* const mjApiService = new MJApiService();
|
||
*
|
||
* // 提交图像生成请求
|
||
* const taskId = await mjApiService.SubmitMJImagine(
|
||
* "local-task-id",
|
||
* "a futuristic cityscape at sunset, hyperrealistic style"
|
||
* );
|
||
*
|
||
* // 查询任务状态
|
||
* const taskStatus = await mjApiService.GetMJAPITaskById(taskId, "local-task-id");
|
||
*/
|
||
export class MJApiService extends MJBasic {
|
||
bootType: 'NIJI_JOURNEY' | 'MID_JOURNEY'
|
||
|
||
imagineUrl!: string
|
||
fetchTaskUrl!: string
|
||
describeUrl!: string
|
||
token!: string
|
||
|
||
constructor() {
|
||
super()
|
||
this.bootType = 'MID_JOURNEY'
|
||
}
|
||
|
||
//#region InitMJSetting
|
||
/**
|
||
* 初始化MJ设置
|
||
*/
|
||
async InitMJSetting(): Promise<void> {
|
||
await this.GetMJGeneralSetting()
|
||
|
||
// 获取当前机器人类型
|
||
this.bootType =
|
||
this.mjGeneralSetting?.robot == MJRobotType.NIJI ? 'NIJI_JOURNEY' : 'MID_JOURNEY'
|
||
|
||
// 再 MJ API 模式下 获取对应的数据
|
||
if (this.mjGeneralSetting?.outputMode == ImageGenerateMode.MJ_API) {
|
||
await this.GetApiSetting()
|
||
if (
|
||
!this.mjApiSetting ||
|
||
isEmpty(this.mjApiSetting.apiUrl) ||
|
||
isEmpty(this.mjApiSetting.apiKey)
|
||
) {
|
||
throw new Error('没有找到对应的API的配置,请检查 ‘设置 -> MJ设置’ 配置!')
|
||
}
|
||
let apiProvider = GetApiDefineDataById(this.mjApiSetting.apiUrl as string)
|
||
if (apiProvider.mj_url == null) {
|
||
throw new Error('当前API不支持MJ出图,请检查 ‘设置 -> MJ设置’ 配置!')
|
||
}
|
||
this.imagineUrl = apiProvider.mj_url.imagine
|
||
this.describeUrl = apiProvider.mj_url.describe
|
||
this.fetchTaskUrl = apiProvider.mj_url.once_get_task
|
||
this.token = this.mjApiSetting.apiKey
|
||
} else if (this.mjGeneralSetting?.outputMode == ImageGenerateMode.MJ_PACKAGE) {
|
||
await this.GetMJPackageSetting()
|
||
if (
|
||
!this.mjPackageSetting ||
|
||
isEmpty(this.mjPackageSetting.selectPackage) ||
|
||
isEmpty(this.mjPackageSetting.packageToken)
|
||
) {
|
||
throw new Error(
|
||
'没有找到对应的生图包的配置或配置有误,请检查 ‘设置 -> MJ设置 -> 生图包模式’ 配置!'
|
||
)
|
||
}
|
||
|
||
let mjProvider = GetApiDefineDataById(this.mjPackageSetting.selectPackage)
|
||
if (!mjProvider.isPackage) {
|
||
throw new Error('当前选择的包不支持,请检查 ‘设置 -> MJ设置 -> 生图包模式’ 配置!')
|
||
}
|
||
|
||
if (mjProvider.mj_url == null) {
|
||
throw new Error('当前选择的包不支持,请检查 ‘设置 -> MJ设置 -> 生图包模式’ 配置!')
|
||
}
|
||
this.imagineUrl = mjProvider.mj_url.imagine
|
||
this.describeUrl = mjProvider.mj_url.describe
|
||
this.fetchTaskUrl = mjProvider.mj_url.once_get_task
|
||
this.token = this.mjPackageSetting.packageToken
|
||
} else if (this.mjGeneralSetting?.outputMode == ImageGenerateMode.LOCAL_MJ) {
|
||
await this.GetMjLocalSetting()
|
||
if (
|
||
this.mjLocalSetting == null ||
|
||
isEmpty(this.mjLocalSetting.requestUrl) ||
|
||
isEmpty(this.mjLocalSetting.token)
|
||
) {
|
||
throw new Error(
|
||
'本地代理模式的设置不完善或配置错误,请检查 ‘设置 -> MJ设置 -> 本地代理模式’ 配置!'
|
||
)
|
||
}
|
||
this.mjLocalSetting.requestUrl.endsWith('/')
|
||
? this.mjLocalSetting.requestUrl.slice(0, -1)
|
||
: this.mjLocalSetting.requestUrl
|
||
|
||
this.imagineUrl = this.mjLocalSetting.requestUrl + '/mj/submit/imagine'
|
||
this.describeUrl = this.mjLocalSetting.requestUrl + '/mj/submit/describe'
|
||
this.fetchTaskUrl = this.mjLocalSetting.requestUrl + '/mj/task/${id}/fetch'
|
||
this.token = this.mjLocalSetting.token
|
||
} else if (this.mjGeneralSetting?.outputMode == ImageGenerateMode.REMOTE_MJ) {
|
||
await this.GetMjRemoteSetting()
|
||
|
||
this.imagineUrl = define.remotemj_api + 'mj/submit/imagine'
|
||
this.describeUrl = define.remotemj_api + 'mj/submit/describe'
|
||
this.fetchTaskUrl = define.remotemj_api + 'mj/task/${id}/fetch'
|
||
this.token = define.remote_token
|
||
} else {
|
||
throw new Error('当前的MJ出图模式不支持,请检查 ‘设置 -> MJ设置’ 配置!')
|
||
}
|
||
}
|
||
|
||
//#endregion
|
||
|
||
//#region 获取对应的任务,通过ID
|
||
/**
|
||
* 通过ID获取MidJourney API任务的状态和结果
|
||
*
|
||
* 该方法向MidJourney API发送请求,获取指定任务ID的状态信息,包括进度、图像URL和错误信息等。
|
||
* 在获取成功后,会将结果格式化为标准响应对象。如果任务失败,则会相应地更新内部任务状态记录。
|
||
*
|
||
* @param {string} taskId - MidJourney API的任务ID,用于查询API任务状态
|
||
* @param {string} backTaskId - 内部系统的任务ID,用于更新本地任务状态记录
|
||
* @returns {Promise<MJ.MJResponseToFront>} 标准化的任务状态响应对象,包含进度、状态、图像URL等信息
|
||
* @throws {Error} 如果API请求失败或返回不可解析的数据
|
||
*
|
||
* @example
|
||
* try {
|
||
* const taskStatus = await mjApiService.GetMJAPITaskById("task-123", "local-456");
|
||
* if (taskStatus.code === 1 && taskStatus.progress === 100) {
|
||
* console.log("任务完成,图像URL:", taskStatus.imageShow);
|
||
* } else {
|
||
* console.log("任务进度:", taskStatus.progress, "%");
|
||
* }
|
||
* } catch (error) {
|
||
* console.error("获取任务状态失败:", error.message);
|
||
* }
|
||
*/
|
||
async GetMJAPITaskById(taskId: string, backTaskId: string): Promise<MJ.MJResponseToFront> {
|
||
try {
|
||
await this.InitMJSetting()
|
||
let APIDescribeUrl = this.fetchTaskUrl.replace('${id}', taskId)
|
||
|
||
// 拼接headers
|
||
let headers = {
|
||
Authorization: this.token
|
||
}
|
||
// 开始请求
|
||
let res = await axios.get(APIDescribeUrl, {
|
||
headers: headers
|
||
})
|
||
let resData = res.data
|
||
|
||
let progress =
|
||
resData.progress && resData.progress.length > 0
|
||
? parseInt(resData.progress.slice(0, -1))
|
||
: 0
|
||
|
||
let status = resData.status.toLowerCase()
|
||
let code = status == 'failure' || status == 'cancel' ? 0 : 1
|
||
// 失败
|
||
if (code == 0) {
|
||
if (!isEmpty(backTaskId)) {
|
||
this.taskListService.UpdateTaskStatus({
|
||
id: backTaskId,
|
||
status: BookBackTaskStatus.FAIL,
|
||
errorMessage: resData.message
|
||
})
|
||
}
|
||
}
|
||
let resObj = {
|
||
type: MJRespoonseType.UPDATED,
|
||
progress: isNaN(progress) ? 0 : progress,
|
||
category: this.mjGeneralSetting?.outputMode,
|
||
imageClick: resData.imageUrl,
|
||
imageShow: resData.imageUrl,
|
||
imagePath: resData.imageUrl,
|
||
imageUrls: resData.imageUrls
|
||
? resData.imageUrls
|
||
.filter((item) => item.url != null && !isEmpty(item.url))
|
||
.map((item) => item.url)
|
||
: [],
|
||
messageId: taskId,
|
||
status: status,
|
||
code: code,
|
||
prompt: resData.prompt == '' ? resData.promptEn : resData.prompt,
|
||
message: resData.failReason,
|
||
mjApiUrl: this.fetchTaskUrl
|
||
} as MJ.MJResponseToFront
|
||
return resObj
|
||
} catch (error) {
|
||
throw error
|
||
}
|
||
}
|
||
//#endregion
|
||
|
||
//#region MJ反推相关操作
|
||
|
||
/**
|
||
* 提交MidJourney图像描述(反推)任务
|
||
*
|
||
* 该方法根据当前设置的输出模式,将图像发送到MidJourney进行描述分析(反推提示词)。
|
||
* 目前仅支持API模式,其他模式将抛出错误。在提交请求前会先初始化MJ设置。
|
||
*
|
||
* @param {MJ.APIDescribeParams} param - 包含任务ID和base64编码图像的参数对象
|
||
* @returns {Promise<string>} 成功时返回API任务结果ID,队列已满时返回"23"
|
||
* @throws {Error} 如果当前输出模式不支持或API调用失败时抛出错误
|
||
*
|
||
* @example
|
||
* try {
|
||
* const params = {
|
||
* taskId: "task-123",
|
||
* image: "data:image/png;base64,iVBORw0KGgo..."
|
||
* };
|
||
* const resultId = await mjApiService.SubmitMJDescribe(params);
|
||
* console.log("提交成功,任务ID:", resultId);
|
||
* } catch (error) {
|
||
* console.error("提交反推任务失败:", error.message);
|
||
* }
|
||
*/
|
||
async SubmitMJDescribe(param: MJ.APIDescribeParams): Promise<string> {
|
||
await this.InitMJSetting()
|
||
let res: string
|
||
switch (this.mjGeneralSetting?.outputMode) {
|
||
case ImageGenerateMode.MJ_API:
|
||
res = await this.SubmitMJDescribeAPI(param)
|
||
break
|
||
default:
|
||
throw new Error('MJ反推的类型不支持,反推只支持,API和代理模式')
|
||
}
|
||
return res
|
||
}
|
||
|
||
/**
|
||
* 生成MidJourney描述请求的主体和配置
|
||
*
|
||
* 该方法根据提供的base64图像和当前MJ设置,生成用于调用MidJourney描述API的请求主体和HTTP配置。
|
||
* 配置会根据当前输出模式自动调整,并包含必要的认证信息和内容类型。
|
||
*
|
||
* @param {string} imageBase64 - base64编码的图像字符串,用于图像描述/反推
|
||
* @returns {{body: MJAPIDescribeRequestBody, config: Object}} 返回包含请求体和配置的对象
|
||
* @throws {Error} 如果当前MJ输出模式不支持,将抛出错误
|
||
*
|
||
* @example
|
||
* const imageBase64 = "data:image/png;base64,iVBORw0KGgo...";
|
||
* const { body, config } = mjApiService.GenerateDescribeRequestBody(imageBase64);
|
||
* const response = await axios.post(describeUrl, body, config);
|
||
*/
|
||
GenerateDescribeRequestBody(imageBase64: string): {
|
||
body: MJAPIDescribeRequestBody
|
||
config: any
|
||
} {
|
||
// 提交API的反推
|
||
let data = {
|
||
botType: this.bootType,
|
||
base64: imageBase64,
|
||
accountFilter: {
|
||
modes: [this.mjApiSetting?.apiSpeed == MJSpeed.FAST ? 'FAST' : 'RELAX'],
|
||
remark: global.machineId,
|
||
instanceId: ''
|
||
} as AccountFilter
|
||
}
|
||
let config = {
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
}
|
||
|
||
if (this.mjGeneralSetting?.outputMode == ImageGenerateMode.MJ_API) {
|
||
delete data.accountFilter.remark
|
||
delete data.accountFilter.instanceId
|
||
config.headers['Authorization'] = this.token
|
||
} else {
|
||
throw new Error('MJ出图的类型不支持')
|
||
}
|
||
return {
|
||
body: data,
|
||
config: config
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 通过API提交MidJourney图像描述(反推)请求
|
||
*
|
||
* 该方法发送图像到MidJourney API进行描述(反推)分析,将base64编码的图像数据发送到API,
|
||
* 并处理返回结果。根据API响应状态码,会相应地更新任务状态记录并返回结果。
|
||
*
|
||
* 支持以下特殊响应处理:
|
||
* - 队列已满(code=23): 更新任务状态为RECONNECT,返回"23"
|
||
* - 请求失败: 更新任务状态为FAIL,抛出错误
|
||
* - 请求成功: 更新任务状态为RUNNING,返回结果ID
|
||
*
|
||
* @param {MJ.APIDescribeParams} param - 包含任务ID和base64编码图像数据的参数对象
|
||
* @returns {Promise<string>} 成功时返回API任务结果ID,队列已满时返回"23"
|
||
* @throws {Error} 如果API返回失败状态码或错误描述
|
||
*
|
||
* @example
|
||
* try {
|
||
* const params = {
|
||
* taskId: "task-123",
|
||
* image: "data:image/png;base64,iVBORw0KGgo..."
|
||
* };
|
||
* const taskResultId = await mjApiService.SubmitMJDescribeAPI(params);
|
||
* if (taskResultId === "23") {
|
||
* // 队列已满,需要重试
|
||
* } else {
|
||
* // 任务提交成功,可以用taskResultId查询结果
|
||
* }
|
||
* } catch (error) {
|
||
* console.error("图像描述请求失败:", error.message);
|
||
* }
|
||
*/
|
||
async SubmitMJDescribeAPI(param: MJ.APIDescribeParams): Promise<string> {
|
||
// 获取body和config
|
||
let { body, config } = this.GenerateDescribeRequestBody(param.image)
|
||
// 开始请求
|
||
let res = await axios.post(this.describeUrl, body, config)
|
||
|
||
// 某些API的返回的code为23,表示队列已满,需要重新请求
|
||
if (res.data.code == 23) {
|
||
this.taskListService.UpdateTaskStatus({
|
||
id: param.taskId,
|
||
status: BookBackTaskStatus.RECONNECT
|
||
})
|
||
return '23'
|
||
}
|
||
|
||
if (res.data.code != 1 && res.data.code != 22) {
|
||
this.taskListService.UpdateTaskStatus({
|
||
id: param.taskId,
|
||
status: BookBackTaskStatus.FAIL,
|
||
errorMessage: res.data.description
|
||
})
|
||
throw new Error(res.data.description)
|
||
}
|
||
|
||
this.taskListService.UpdateTaskStatus({
|
||
id: param.taskId,
|
||
status: BookBackTaskStatus.RUNNING
|
||
})
|
||
|
||
return res.data.result as string
|
||
}
|
||
//#endregion
|
||
|
||
//#region 提交MJ生图任务
|
||
|
||
/**
|
||
* 提交MidJourney图像生成任务
|
||
*
|
||
* 该方法根据当前设置的输出模式,发送提示词到MidJourney进行图像生成。
|
||
* 目前仅支持API模式,其他模式将抛出错误。在提交请求前会先初始化MJ设置。
|
||
*
|
||
* @param {string} taskId - 任务ID,用于追踪和更新任务状态
|
||
* @param {string} prompt - 用于生成图像的提示词文本
|
||
* @returns {Promise<string>} 成功时返回API任务结果ID,队列已满时返回"23"
|
||
* @throws {Error} 如果当前输出模式不支持或API调用失败时抛出错误
|
||
*
|
||
* @example
|
||
* try {
|
||
* const resultId = await mjApiService.SubmitMJImagine("task-123", "a beautiful sunset in watercolor style");
|
||
* console.log("提交成功,任务ID:", resultId);
|
||
* } catch (error) {
|
||
* console.error("提交生图任务失败:", error.message);
|
||
* }
|
||
*/
|
||
async SubmitMJImagine(taskId: string, prompt: string): Promise<string> {
|
||
await this.InitMJSetting()
|
||
let res: string
|
||
switch (this.mjGeneralSetting?.outputMode) {
|
||
case ImageGenerateMode.MJ_API:
|
||
case ImageGenerateMode.MJ_PACKAGE:
|
||
case ImageGenerateMode.REMOTE_MJ:
|
||
case ImageGenerateMode.LOCAL_MJ:
|
||
res = await this.SubmitMJImagineAPI(taskId, prompt)
|
||
break
|
||
default:
|
||
throw new Error('MJ出图的类型不支持')
|
||
}
|
||
return res
|
||
}
|
||
|
||
/**
|
||
* 生成MidJourney API的imagine请求体和配置
|
||
*
|
||
* 该方法根据当前MJ设置生成用于调用imagine API的请求主体和HTTP配置。
|
||
* 配置包含必要的认证信息和内容类型,根据输出模式自动调整请求参数。
|
||
*
|
||
* @param {string} prompt - 用于生成图像的提示词文本
|
||
* @returns {{body: MJAPIImagineRequestBody, config: Object}} 返回包含请求体和配置的对象
|
||
* @throws {Error} 如果当前MJ输出模式不支持,将抛出错误
|
||
*
|
||
* @example
|
||
* const { body, config } = mjApiService.GenerateImagineRequestBody("a beautiful sunset");
|
||
* const response = await axios.post(imagineUrl, body, config);
|
||
*
|
||
* @note 当前实现有问题,缺少prompt参数,需要修改方法签名为GenerateImagineRequestBody(prompt: string)
|
||
*/
|
||
GenerateImagineRequestBody(prompt: string): {
|
||
body: MJAPIImagineRequestBody
|
||
config: any
|
||
} {
|
||
// 提交API的出图任务
|
||
let data = {
|
||
botType: this.bootType,
|
||
prompt: prompt,
|
||
accountFilter: {
|
||
modes: [this.mjApiSetting?.apiSpeed == MJSpeed.FAST ? 'FAST' : 'RELAX'],
|
||
remark: global.machineId ?? '',
|
||
instanceId: ''
|
||
} as AccountFilter
|
||
}
|
||
let config = {
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
}
|
||
|
||
let useTransfer = false
|
||
|
||
if (this.mjGeneralSetting?.outputMode == ImageGenerateMode.MJ_API) {
|
||
delete data.accountFilter.remark
|
||
delete data.accountFilter.instanceId
|
||
config.headers['Authorization'] = this.token
|
||
useTransfer = false
|
||
} else if (this.mjGeneralSetting?.outputMode == ImageGenerateMode.MJ_PACKAGE) {
|
||
delete data.accountFilter.remark
|
||
delete data.accountFilter.instanceId
|
||
delete data.accountFilter.modes
|
||
config.headers['Authorization'] = this.token
|
||
useTransfer = false
|
||
} else if (this.mjGeneralSetting?.outputMode == ImageGenerateMode.LOCAL_MJ) {
|
||
delete data.accountFilter.remark
|
||
delete data.accountFilter.modes
|
||
delete data.accountFilter.instanceId
|
||
config.headers['mj-api-secret'] = this.token
|
||
useTransfer = false
|
||
} else if (this.mjGeneralSetting?.outputMode == ImageGenerateMode.REMOTE_MJ) {
|
||
config.headers['mj-api-secret'] = this.token
|
||
delete data.accountFilter.modes
|
||
delete data.accountFilter.instanceId
|
||
useTransfer = this.mjRemoteSetting?.isForward ?? false
|
||
} else {
|
||
throw new Error('不支持的MJ出图类型')
|
||
}
|
||
console.log('useTransfer', useTransfer)
|
||
return {
|
||
body: data,
|
||
config: config
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 通过API提交MidJourney图像生成任务
|
||
*
|
||
* 该方法向MidJourney API提交图像生成请求,并处理返回结果。在提交前会验证提示词是否包含非法链接,
|
||
* 然后构造API请求体并发送。根据API响应,会相应地更新任务状态并返回结果。
|
||
*
|
||
* 支持以下特殊响应处理:
|
||
* - 队列已满(code=23): 更新任务状态为RECONNECT,返回"23"
|
||
* - 请求失败: 更新任务状态为FAIL,抛出错误
|
||
* - 请求成功: 更新任务状态为RUNNING,返回结果ID
|
||
*
|
||
* @param {string} taskId - 任务ID,用于更新任务状态记录
|
||
* @param {string} prompt - 发送给MidJourney的图像生成提示词
|
||
* @returns {Promise<string>} 成功时返回API任务结果ID,队列已满时返回"23"
|
||
* @throws {Error} 如果提示词中包含非法链接、API返回错误或返回数据为空
|
||
*
|
||
* @example
|
||
* try {
|
||
* const taskResultId = await mjApiService.SubmitMJImagineAPI("task-123", "a beautiful sunset");
|
||
* if (taskResultId === "23") {
|
||
* // 队列已满,需要重试
|
||
* } else {
|
||
* // 任务提交成功,可以用taskResultId查询结果
|
||
* }
|
||
* } catch (error) {
|
||
* console.error("提交任务失败:", error.message);
|
||
* }
|
||
*/
|
||
async SubmitMJImagineAPI(taskId: string, prompt: string): Promise<string> {
|
||
// 这边校验是不是在提示词包含不正确的链接
|
||
if (prompt.includes('feishu.cn')) {
|
||
throw new Error('提示词里面出现了 feishu.cn 飞书的链接,请检查并复制正确的链接')
|
||
}
|
||
|
||
let { body, config } = this.GenerateImagineRequestBody(prompt)
|
||
|
||
// 开始请求
|
||
let res = await axios.post(this.imagineUrl, body, config)
|
||
let resData = res.data
|
||
|
||
if (this.mjGeneralSetting?.outputMode == ImageGenerateMode.MJ_PACKAGE) {
|
||
if (resData.code == -1 || resData.success == false) {
|
||
throw new Error(resData.message)
|
||
}
|
||
}
|
||
|
||
if (resData == null) {
|
||
throw new Error('返回的数据为空')
|
||
}
|
||
|
||
// 某些API的返回的code为23,表示队列已满,需要重新请求
|
||
if (resData.code == 23) {
|
||
this.taskListService.UpdateTaskStatus({
|
||
id: taskId,
|
||
status: BookBackTaskStatus.RECONNECT
|
||
})
|
||
return '23'
|
||
}
|
||
|
||
if (resData.code != 1 && resData.code != 22) {
|
||
this.taskListService.UpdateTaskStatus({
|
||
id: taskId,
|
||
status: BookBackTaskStatus.FAIL,
|
||
errorMessage: resData.description
|
||
})
|
||
throw new Error(resData.description)
|
||
}
|
||
|
||
this.taskListService.UpdateTaskStatus({
|
||
id: taskId,
|
||
status: BookBackTaskStatus.RUNNING
|
||
})
|
||
|
||
return resData.result as string
|
||
}
|
||
|
||
//#endregion
|
||
}
|