首先来一波感谢:
感谢laf提供赞助,目前可以免费使用Midjourney进行开发和测试。
感谢白夜、米开朗基杨@sealos.io的耐心解答,让我对laf有了更多的使用与了解。
什么是laf?来了解下。
文末有【示例】
开始
废话不多说,进入正题。
laf在做一个活动,可以使用快速上手Midjourney《人人都能接入 Midjourney》。具体活动,可以查看论坛。
原理
原理部分不涉及代码,涉及背景介绍,熟悉的同学可以略过。因为midjourney
官方没有提供API功能,所以有国外大神将开源了一款模拟请求工具,来请求midjourney,laf将这代码集成到内部的npm包中。如果需要github地址,欢迎留言,我私信给您。
所以laf平台,加上下面的实例代码可以很好的快速接入到系统中。
开始
要现有个laf账号,会有免费1月的试用,本次调用mj(以下代指midjourney),所以要在laf.dev
上注册使用,这是运行在新加坡环境上。
代码逻辑
我的核心思想是这样的:先要有个核心与midjourney
交互的代码,上层的我们想做的业务逻辑可以自己定义。
代码
laf 是一个云开发平台,可以将代码暴露成api形式,进行调用,一些其他鉴权等,可以在之前我写的文章中找到灵感:腾讯云开发迁移实录
要做一个云函数,处理与midjourney交互,我这里起名云函数mj-send
import cloud from '@lafjs/cloud'
import { Midjourney, MidjourneyMessage } from 'midjourney'
const SERVER_ID = '' // Midjourney 服务 ID
const CHANNEL_ID = '' // Midjourney 频道 ID
const SALAI_TOKEN = '' // Midjourney 服务 Token
const Limit = 100
const MaxWait = 3
const client = new Midjourney({
ServerId: SERVER_ID,
ChannelId: CHANNEL_ID,
SalaiToken: SALAI_TOKEN,
Debug: true,
SessionId: SALAI_TOKEN,
Limit: Limit,
MaxWait: MaxWait
});
export default async function (ctx: FunctionContext) {
const { type, param } = ctx.body
switch (type) {
case 'RetrieveMessages':
return await RetrieveMessages(param)
case 'imagine':
return await imagine(param)
case 'upscale':
return await upscale(param)
case 'variation':
return await variation(param)
}
}
// 查询最近消息
async function RetrieveMessages(param) {
console.log("RetrieveMessages")
const client = new MidjourneyMessage({
ChannelId: CHANNEL_ID,
SalaiToken: SALAI_TOKEN,
});
const msg = await client.RetrieveMessages();
console.log("RetrieveMessages success ", msg)
return msg
}
// 创建生图任务
async function imagine(param) {
console.log("imagine", param)
const { question, msg_Id } = param
const msg = await client.Imagine(
`[${msg_Id}] ${question}`,
(uri: string, progress: string) => {
console.log("loading", uri, "progress", progress);
}
);
console.log("imagine success ", msg)
return true
}
// upscale 放大图片
async function upscale(param) {
console.log("upscale", param)
const { question, index, id, url } = param
const hash = url.split("_").pop()?.split(".")[0] ?? ""
console.log(hash)
const msg = await client.Upscale(
question,
index,
id,
hash,
(uri: string, progress: string) => {
console.log("loading", uri, "progress", progress);
}
);
console.log("upscale success ", msg)
return msg
}
// variation 变换图片
async function variation(param) {
console.log("variation", param)
const client = new Midjourney({
ServerId: SERVER_ID,
ChannelId: CHANNEL_ID,
SalaiToken: SALAI_TOKEN,
Debug: true,
SessionId: SALAI_TOKEN,
Limit: Limit,
MaxWait: 100
});
const { question, index, id, url } = param
const hash = url.split("_").pop()?.split(".")[0] ?? ""
const msg = await client.Variation(
question,
index,
id,
hash,
(uri: string, progress: string) => {
console.log("loading", uri, "progress", progress);
}
);
console.log("variation success ", msg)
return msg
}
什么?没有账号?仔细看帖,里面有获取方式
什么?已经有了midjourney付费账号,没有key?大佬我们做朋友,留言我来告诉你。
发布即可调用,怎么调用?我把curl给你,你看看结构就懂了,该替换的替换。
画图需要一些时间,这里默认使用的fast,但也是会花时间的,所以成图我们要在之后调用查询接口查看。
question
:就是prompt,这里要输入英文。
关注下参数msg_Id
,后面有用
curl --location --request POST 'https://<你的发布地址服务>' \
--header 'User-Agent: apifox/1.0.0 (https://www.apifox.cn)' \
--header 'Content-Type: application/json' \
--data-raw '{
"type": "imagine",
"param": {
"question": "a dog",
"msg_Id": 1684585158
}
}'
查询
curl --location --request POST 'https://<你的发布地址服务>' \
--header 'User-Agent: apifox/1.0.0 (https://www.apifox.cn)' \
--header 'Content-Type: application/json' \
--data-raw '{
"type":"RetrieveMessages"
}'
如何获取到我刚创建的图呢?先看一下返回的结果。
[
{
"id": "1109565864640008202",
"type": 0,
"content": "**[1684582132] a dog --seed 8925 --v 5** - <@1013684342851117146> (fast)",
"channel_id": "1109368983364313204",
"author": {
"id": "936929561302675456",
"username": "Midjourney Bot",
"global_name": null,
"avatar": "4a79ea7cd151474ff9f6e08339d69380",
"discriminator": "9282",
"public_flags": 589824,
"bot": true,
"avatar_decoration": null
},
"attachments": [
{
"id": "1109565863994077215",
"filename": "johnsonmaureen_1684582132_a_dog_a062b5e2-ab39-40b0-b281-1365695529d5.png",
"size": 4275948,
"url": "https://cdn.discordapp.com/attachments/1109368983364313204/1109565863994077215/johnsonmaureen_1684582132_a_dog_a062b5e2-ab39-40b0-b281-1365695529d5.png",
"proxy_url": "https://media.discordapp.net/attachments/1109368983364313204/1109565863994077215/johnsonmaureen_1684582132_a_dog_a062b5e2-ab39-40b0-b281-1365695529d5.png",
"width": 2048,
"height": 2048,
"content_type": "image/png"
}
]
}
**省略其他数据**
]
返回结果为数组,还记得上面的msg_Id
吗,在JSON结构中查看content
部分,就可以看到 "content": "**[1684582132] a dog --seed 8925 --v 5** - <@1013684342851117146> (fast)",
所以,我们可以用这个字段来进行筛选。$.attachments[0].url
就是成图了。
如果再返回的四张图有一张看得不错,怎么生成大图呢?先给你个表格,告诉你哪是1,哪是3 1 2 3 4
然后来看如何传参
curl --location --request POST 'https://<你的发布地址服务>' \
--header 'User-Agent: apifox/1.0.0 (https://www.apifox.cn)' \
--header 'Content-Type: application/json' \
--data-raw '{
"type": "upscale",
"param": {
"id": "1109460470152319086",
"question":"a dog",
"index": 3,
"url":"https://cdn.discordapp.com/attachments/1109368983364313204/1109460469628022915/johnsonmaureen_1684585158_a_dog_d5b7e35c-0fce-4f7d-b440-35f5602d2f25.png"
}
}'
解释下参数:
id: 是查询返回结果体里的id question,是最开始输入进去的prompt
index: 你想要的第几张图
url:是查询返回结果体里的url,四张图拼一起的那个。
别问我怎么知道的,我都是翻代码看得。
重绘当看上这4张图中某一个的风格或者样式之后,可以根据这个风格重绘。
curl --location --request POST 'https://<你的发布地址服务>' \
--header 'User-Agent: apifox/1.0.0 (https://www.apifox.cn)' \
--header 'Content-Type: application/json' \
--data-raw '{
"type": "variation",
"param": {
"id": "1109460470152319086",
"question": "a dog",
"index": 3,
"url": "https://cdn.discordapp.com/attachments/1109368983364313204/1109460469628022915/johnsonmaureen_1684585158_a_dog_d5b7e35c-0fce-4f7d-b440-35f5602d2f25.png"
}
}'
参数解释不说了,跟上面类似。
至此教程结束了
赶紧去上手做点什么吧,实践出真知~
作品展示
这次其实是laf在组织活动,想我这种平台级别思考大佬的人,不会去钻到某个应用当中,我来看看大家都画了什么图,所以我做了个简陋的页面,本人前端能力有限,代码实现全靠ChatGPT。
上链接:
laf比赛作品大赏
如果觉得不错打个赏吧,后面是答疑环节
问题
我怎么知道mj的图画完没有呢?可以根据
$.attachments[0].width
的尺寸大小来看,如果小于2048,则没有生成完,没生成完其实应该是512。
怎么让图片结果与请求msg_Id绑定?正如上文所说要自己实现一个逻辑去匹配结果,另外还有一个就是用好触发器,他会定时执行你的服务,这样你就可以将数据进行增量处理。
`