koishi-plugin-aka-ai-generator 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +99 -32
- package/lib/providers/yunwu-video.d.ts +2 -2
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -886,7 +886,7 @@ var YunwuVideoProvider = class {
|
|
|
886
886
|
}
|
|
887
887
|
/**
|
|
888
888
|
* 创建视频生成任务
|
|
889
|
-
* API: POST /v1/
|
|
889
|
+
* API: POST /v1/video/create
|
|
890
890
|
*/
|
|
891
891
|
async createVideoTask(prompt, imageUrl, options) {
|
|
892
892
|
const { logger, ctx } = this.config;
|
|
@@ -898,64 +898,127 @@ var YunwuVideoProvider = class {
|
|
|
898
898
|
this.config.apiTimeout,
|
|
899
899
|
logger
|
|
900
900
|
);
|
|
901
|
+
let orientation = "landscape";
|
|
902
|
+
if (options?.aspectRatio === "9:16") {
|
|
903
|
+
orientation = "portrait";
|
|
904
|
+
} else if (options?.aspectRatio === "1:1") {
|
|
905
|
+
orientation = "portrait";
|
|
906
|
+
}
|
|
907
|
+
let duration = options?.duration || 15;
|
|
908
|
+
if (duration < 15) {
|
|
909
|
+
duration = 15;
|
|
910
|
+
} else if (duration > 25) {
|
|
911
|
+
duration = 25;
|
|
912
|
+
} else if (duration <= 20) {
|
|
913
|
+
duration = 15;
|
|
914
|
+
} else {
|
|
915
|
+
duration = 25;
|
|
916
|
+
}
|
|
901
917
|
const requestBody = {
|
|
918
|
+
images: [`data:${mimeType};base64,${imageBase64}`],
|
|
902
919
|
model: this.config.modelId,
|
|
920
|
+
orientation,
|
|
903
921
|
prompt,
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
922
|
+
size: "large",
|
|
923
|
+
// 高清1080p
|
|
924
|
+
duration,
|
|
925
|
+
watermark: true,
|
|
926
|
+
// 默认优先无水印,出错会兜底到有水印
|
|
927
|
+
private: false
|
|
928
|
+
// 默认视频会发布
|
|
907
929
|
};
|
|
908
|
-
if (options?.fps) {
|
|
909
|
-
requestBody.fps = options.fps;
|
|
910
|
-
}
|
|
911
930
|
logger?.info("提交视频生成任务", {
|
|
912
931
|
model: this.config.modelId,
|
|
913
932
|
promptLength: prompt.length,
|
|
914
933
|
duration: requestBody.duration,
|
|
915
|
-
|
|
934
|
+
orientation: requestBody.orientation
|
|
916
935
|
});
|
|
917
936
|
const response = await ctx.http.post(
|
|
918
|
-
`${this.config.apiBase}/v1/
|
|
937
|
+
`${this.config.apiBase}/v1/video/create`,
|
|
919
938
|
requestBody,
|
|
920
939
|
{
|
|
921
940
|
headers: {
|
|
922
941
|
"Authorization": `Bearer ${this.config.apiKey}`,
|
|
923
|
-
"Content-Type": "application/json"
|
|
942
|
+
"Content-Type": "application/json",
|
|
943
|
+
"Accept": "application/json"
|
|
924
944
|
},
|
|
925
945
|
timeout: this.config.apiTimeout * 1e3
|
|
926
946
|
}
|
|
927
947
|
);
|
|
928
948
|
if (response.error) {
|
|
929
|
-
|
|
949
|
+
const errorMsg = response.error.message || response.error.type || "创建任务失败";
|
|
950
|
+
throw new Error(sanitizeString(errorMsg));
|
|
951
|
+
}
|
|
952
|
+
if (response.status && response.status >= 400) {
|
|
953
|
+
const errorMsg = response.data?.error?.message || response.data?.error || response.statusText || "创建任务失败";
|
|
954
|
+
logger?.error("API 返回错误状态", { status: response.status, error: errorMsg, response: response.data });
|
|
955
|
+
throw new Error(sanitizeString(errorMsg));
|
|
930
956
|
}
|
|
931
|
-
const taskId = response.id || response.
|
|
957
|
+
const taskId = response.id || response.data?.id;
|
|
932
958
|
if (!taskId) {
|
|
933
959
|
logger?.error("未能获取任务ID", { response });
|
|
934
|
-
throw new Error("未能获取任务ID");
|
|
960
|
+
throw new Error("未能获取任务ID,请检查 API 响应格式");
|
|
935
961
|
}
|
|
936
962
|
logger?.info("视频任务已创建", { taskId });
|
|
937
963
|
return taskId;
|
|
938
964
|
} catch (error) {
|
|
939
965
|
logger?.error("创建视频任务失败", { error: sanitizeError(error) });
|
|
940
|
-
|
|
966
|
+
let errorMessage = error.message || "创建视频任务失败";
|
|
967
|
+
if (error.response) {
|
|
968
|
+
const responseData = error.response.data;
|
|
969
|
+
if (responseData?.error) {
|
|
970
|
+
errorMessage = responseData.error.message || responseData.error.type || errorMessage;
|
|
971
|
+
} else if (responseData?.message) {
|
|
972
|
+
errorMessage = responseData.message;
|
|
973
|
+
}
|
|
974
|
+
} else if (error.response?.data?.error) {
|
|
975
|
+
errorMessage = error.response.data.error.message || error.response.data.error.type || errorMessage;
|
|
976
|
+
}
|
|
977
|
+
throw new Error(`创建视频任务失败: ${sanitizeString(errorMessage)}`);
|
|
941
978
|
}
|
|
942
979
|
}
|
|
943
980
|
/**
|
|
944
981
|
* 查询任务状态
|
|
945
|
-
* API: GET /v1/
|
|
982
|
+
* API: GET /v1/video/{taskId} (根据创建端点的模式推断)
|
|
946
983
|
*/
|
|
947
984
|
async queryTaskStatus(taskId) {
|
|
948
985
|
const { logger, ctx } = this.config;
|
|
949
986
|
try {
|
|
950
|
-
const
|
|
951
|
-
|
|
952
|
-
{
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
987
|
+
const possibleEndpoints = [
|
|
988
|
+
`/v1/video/${taskId}`,
|
|
989
|
+
`/v1/video/status/${taskId}`,
|
|
990
|
+
`/v1/videos/generations/${taskId}`
|
|
991
|
+
];
|
|
992
|
+
let response = null;
|
|
993
|
+
let lastError = null;
|
|
994
|
+
for (const endpoint of possibleEndpoints) {
|
|
995
|
+
try {
|
|
996
|
+
response = await ctx.http.get(
|
|
997
|
+
`${this.config.apiBase}${endpoint}`,
|
|
998
|
+
{
|
|
999
|
+
headers: {
|
|
1000
|
+
"Authorization": `Bearer ${this.config.apiKey}`,
|
|
1001
|
+
"Accept": "application/json"
|
|
1002
|
+
},
|
|
1003
|
+
timeout: this.config.apiTimeout * 1e3
|
|
1004
|
+
}
|
|
1005
|
+
);
|
|
1006
|
+
if (response && !response.error) {
|
|
1007
|
+
logger?.debug("查询任务状态成功", { taskId, endpoint });
|
|
1008
|
+
break;
|
|
1009
|
+
}
|
|
1010
|
+
} catch (err) {
|
|
1011
|
+
lastError = err;
|
|
1012
|
+
if (err.response?.status === 404 && possibleEndpoints.indexOf(endpoint) < possibleEndpoints.length - 1) {
|
|
1013
|
+
logger?.debug("尝试下一个查询端点", { taskId, endpoint, nextEndpoint: possibleEndpoints[possibleEndpoints.indexOf(endpoint) + 1] });
|
|
1014
|
+
continue;
|
|
1015
|
+
}
|
|
1016
|
+
throw err;
|
|
957
1017
|
}
|
|
958
|
-
|
|
1018
|
+
}
|
|
1019
|
+
if (!response) {
|
|
1020
|
+
throw lastError || new Error("所有查询端点都失败");
|
|
1021
|
+
}
|
|
959
1022
|
const status = response.status || response.data?.status || "pending";
|
|
960
1023
|
const videoUrl = response.video_url || response.url || response.data?.video_url || response.data?.url;
|
|
961
1024
|
return {
|
|
@@ -967,7 +1030,11 @@ var YunwuVideoProvider = class {
|
|
|
967
1030
|
};
|
|
968
1031
|
} catch (error) {
|
|
969
1032
|
logger?.error("查询任务状态失败", { taskId, error: sanitizeError(error) });
|
|
970
|
-
|
|
1033
|
+
let errorMessage = error.message || "查询任务失败";
|
|
1034
|
+
if (error.response?.data?.error) {
|
|
1035
|
+
errorMessage = error.response.data.error.message || error.response.data.error.type || errorMessage;
|
|
1036
|
+
}
|
|
1037
|
+
throw new Error(`查询任务失败: ${sanitizeString(errorMessage)}`);
|
|
971
1038
|
}
|
|
972
1039
|
}
|
|
973
1040
|
/**
|
|
@@ -1595,19 +1662,19 @@ var Config = import_koishi2.Schema.intersect([
|
|
|
1595
1662
|
videoStyles: import_koishi2.Schema.array(import_koishi2.Schema.object({
|
|
1596
1663
|
commandName: import_koishi2.Schema.string().required().description("命令名称").role("table-cell", { width: 100 }),
|
|
1597
1664
|
prompt: import_koishi2.Schema.string().role("textarea", { rows: 2 }).required().description("视频描述 prompt"),
|
|
1598
|
-
duration: import_koishi2.Schema.number().
|
|
1665
|
+
duration: import_koishi2.Schema.number().default(15).description("视频时长(秒,仅支持 15 或 25)"),
|
|
1599
1666
|
aspectRatio: import_koishi2.Schema.string().description("宽高比(如 16:9)")
|
|
1600
1667
|
})).role("table").default([
|
|
1601
1668
|
{
|
|
1602
1669
|
commandName: "慢动作视频",
|
|
1603
1670
|
prompt: "缓慢流畅的运镜,展现细节和质感",
|
|
1604
|
-
duration:
|
|
1671
|
+
duration: 25,
|
|
1605
1672
|
aspectRatio: "16:9"
|
|
1606
1673
|
},
|
|
1607
1674
|
{
|
|
1608
1675
|
commandName: "快节奏视频",
|
|
1609
1676
|
prompt: "快速动态的场景变化,充满活力",
|
|
1610
|
-
duration:
|
|
1677
|
+
duration: 15,
|
|
1611
1678
|
aspectRatio: "16:9"
|
|
1612
1679
|
}
|
|
1613
1680
|
]).description("视频风格预设")
|
|
@@ -2137,7 +2204,7 @@ ${infoParts.join("\n")}`;
|
|
|
2137
2204
|
}
|
|
2138
2205
|
}
|
|
2139
2206
|
if (config.enableVideoGeneration && videoProvider) {
|
|
2140
|
-
ctx.command("图生视频 [img:text]", "根据图片和描述生成视频").option("duration", "-d <duration:number> 视频时长(
|
|
2207
|
+
ctx.command("图生视频 [img:text]", "根据图片和描述生成视频").option("duration", "-d <duration:number> 视频时长(15 或 25 秒)").option("ratio", "-r <ratio:string> 宽高比(16:9, 9:16, 1:1)").action(async ({ session, options }, img) => {
|
|
2141
2208
|
if (!session?.userId) return "会话无效";
|
|
2142
2209
|
const userId = session.userId;
|
|
2143
2210
|
const userName = session.username || userId || "未知用户";
|
|
@@ -2177,9 +2244,9 @@ ${infoParts.join("\n")}`;
|
|
|
2177
2244
|
}
|
|
2178
2245
|
prompt = text;
|
|
2179
2246
|
}
|
|
2180
|
-
const duration = options?.duration ||
|
|
2181
|
-
if (duration
|
|
2182
|
-
return "
|
|
2247
|
+
const duration = options?.duration || 15;
|
|
2248
|
+
if (duration !== 15 && duration !== 25) {
|
|
2249
|
+
return "视频时长必须是 15 或 25 秒";
|
|
2183
2250
|
}
|
|
2184
2251
|
const ratio = options?.ratio || "16:9";
|
|
2185
2252
|
const validRatios = ["16:9", "9:16", "1:1"];
|
|
@@ -2289,7 +2356,7 @@ ${infoParts.join("\n")}`;
|
|
|
2289
2356
|
finalPrompt,
|
|
2290
2357
|
imageUrls[0],
|
|
2291
2358
|
{
|
|
2292
|
-
duration: style.duration ||
|
|
2359
|
+
duration: style.duration || 15,
|
|
2293
2360
|
aspectRatio: style.aspectRatio || "16:9"
|
|
2294
2361
|
},
|
|
2295
2362
|
config.videoMaxWaitTime
|
|
@@ -9,12 +9,12 @@ export declare class YunwuVideoProvider implements VideoProvider {
|
|
|
9
9
|
constructor(config: YunwuVideoConfig);
|
|
10
10
|
/**
|
|
11
11
|
* 创建视频生成任务
|
|
12
|
-
* API: POST /v1/
|
|
12
|
+
* API: POST /v1/video/create
|
|
13
13
|
*/
|
|
14
14
|
createVideoTask(prompt: string, imageUrl: string, options?: VideoGenerationOptions): Promise<string>;
|
|
15
15
|
/**
|
|
16
16
|
* 查询任务状态
|
|
17
|
-
* API: GET /v1/
|
|
17
|
+
* API: GET /v1/video/{taskId} (根据创建端点的模式推断)
|
|
18
18
|
*/
|
|
19
19
|
queryTaskStatus(taskId: string): Promise<VideoTaskStatus>;
|
|
20
20
|
/**
|