返回顶部
首页 > 资讯 > 后端开发 > JAVA >Java调用Midjourney进行AI画图原生版抓包实现支持中文
  • 666
分享到

Java调用Midjourney进行AI画图原生版抓包实现支持中文

人工智能midjourneyjava 2023-09-18 15:09:43 666人浏览 八月长安
摘要

用途介绍 Midjourney是一个目前优秀的ai画图工具,不挂梯无法直接访问 本代码主要用于搭建镜像站使用 适合人群 本代码不适合新手,建议使用过okhttp、且具有二开能力的同学使用~ 实现原理 通过调用发送信息接口发送请求,通过轮询房

用途介绍

Midjourney是一个目前优秀的ai画图工具,不挂梯无法直接访问

本代码主要用于搭建镜像站使用

适合人群

本代码不适合新手,建议使用过okhttp、且具有二开能力的同学使用~

实现原理

通过调用发送信息接口发送请求,通过轮询房间消息接口判断是否作图完成

发送的时候带上我们存储好的cookie信息即可

轮询房间消息接口是为了避免模拟网页实际的websocket连接,那玩意解密没解出来...

准备

开通Midjourney会员的Discord账号

新建很多房间(因为房间消息接口默认就50条数据,也就是说每个房间最多并行50个图片生成任务)

功能预览

上代码

代码功能:

调用Midjourney生成并将生成好的内容发送微信消息给用户(这个是在微信公众号平台做的,大家做网页版的话自己改下输出到网页就可以)

进行合规性检测(生成点不得了的东西后果你懂得,这里用的是百度的文本审核服务,5万次以下免费)

3. 如果用户输入是中文,就翻译为英文再发送

midjourneyLog

midjourney_log表字段如下代码所示

需要使用通用mapper哦~

package com.example.midjourney.bean.pojo;import lombok.Data;import tk.mybatis.mapper.annotation.NameStyle;import tk.mybatis.mapper.code.Style;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.persistence.Table;import java.util.Date;@Data@NameStyle(Style.camelhump)@Table(name = "midjourney_log")public class MidjourneyLog {    @Id    @GeneratedValue(strategy = GenerationType.IDENTITY)    private Long id;    private Integer memberId;    private String channel;    private String prompt;    private Integer type;    private Integer status;    private String imgFile;    private Integer roomId;    private String uuid;    private Date createTime;}

MidJourneyBiz

请求唯一id没啥规律,一段时间内不一致就行,试了没啥校验

另外请求头除了cookie需要保证和 authorization、x-super-properties对应上,三者对应上实测2个月都不会掉线!

package com.example.midjourney.biz;import cn.hutool.core.img.ImgUtil;import cn.hutool.core.io.FileUtil;import com.alibaba.fastjson.JSON;import com.example.midjourney.bean.BaiduTextCensor;import com.example.midjourney.bean.BaiduTextCensorData;import com.example.midjourney.bean.MidMsg;import com.example.midjourney.bean.pojo.Discord;import com.example.midjourney.bean.pojo.MidjourneyLog;import com.example.midjourney.bean.pojo.RoomInfo;import com.example.midjourney.contant.Constant;import com.example.midjourney.enums.MedjourneyLogType;import com.example.midjourney.service.DiscordService;import com.example.midjourney.service.MidjourneyLogService;import com.example.midjourney.service.MemberService;import com.example.midjourney.service.RoomInfoService;import com.example.midjourney.util.*;import com.google.common.base.Joiner;import com.google.common.base.Splitter;import com.google.common.collect.Lists;import lombok.SneakyThrows;import lombok.extern.slf4j.Slf4j;import okhttp3.*;import org.apache.logging.log4j.util.Strings;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Service;import javax.annotation.Resource;import javax.imageio.ImageIO;import java.awt.*;import java.awt.image.BufferedImage;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.math.BigDecimal;import java.net.InetSocketAddress;import java.net.Proxy;import java.util.List;import java.util.Objects;import java.util.Set;import java.util.UUID;import java.util.stream.Collectors;import static com.example.midjourney.contant.Constant.*;@Slf4j@Servicepublic class MidJourneyBiz {    private static BigDecimal bigDecimal = new BigDecimal("1099765990370980513")            .add(new BigDecimal(System.currentTimeMillis()));    @Resource    private WeChatBiz weChatBiz;    @Resource    private TranslateBiz translateBiz;    @Resource    private MidjourneyLogService midjourneyLogService;    @Resource    private MemberService memberService;    @Resource    private RoomInfoService roomInfoService;    @Resource    private DiscordService discordService;    public void buildImg(Long id) {        MidjourneyLog midjourneyLog = midjourneyLogService.findById(id);        try {            if (midjourneyLog.getStatus() != 0) {                return;            }            if (isFailMsg(midjourneyLog.getChannel(), midjourneyLog)) {                log.info("[失败提示] 消息校验不通过 log:{}", midjourneyLog);                sendBuildFail(midjourneyLog);                return;            }            String prompt = midjourneyLog.getPrompt();            if (midjourneyLog.getType() == MedjourneyLogType.BIG_IMG.getCode()) {                downImg(midjourneyLog);                return;            }            BaiduTextCensor censor = null;            if (TextUtil.isHaveChinese(prompt)) {                censor = BaiduUtil.textCensor(prompt);            }            prompt = cleanMsg(prompt);            if (isBlackWord(prompt)) {                sendSensitive(midjourneyLog);                return;            }            if (Objects.isNull(censor)) {                censor = BaiduUtil.textCensor(prompt);            }            midjourneyLogService.updatePrompt(midjourneyLog, prompt);            if (censor.getConclusionType() == 2 || censor.getConclusionType() == 3) {                sendSensitive(midjourneyLog, censor);                return;            }            if (midjourneyLog.getType() == MedjourneyLogType.ITERATIVE.getCode()) {                //暂不支持                sendBuildFail(midjourneyLog);                return;            }            RoomInfo roomInfo = roomInfoService.findIdleRoom();            if (Objects.isNull(roomInfo)) {                log.error("[并发超出警报] 当前并发次数已经无法满足!!!");                sendBuildFail(midjourneyLog);                return;            }            if (sendMsg(prompt, roomInfo)) {                midjourneyLogService.updateRoom(midjourneyLog, roomInfo);            } else {                log.error("[发送失败] 发送信息失败,请检查");                sendBuildFail(midjourneyLog);            }        } catch (Throwable t) {            sendBuildFail(midjourneyLog);        }    }    @SneakyThrows    private void downImg(MidjourneyLog midjourneyLog) {        MidjourneyLog lastLog = midjourneyLogService.findLastNormalLog(midjourneyLog.getMemberId());        log.info("[下载图片] {}", lastLog);        if (Objects.isNull(lastLog)) {            log.info("[失败提示] 找不到上一次的图片 log:{}", midjourneyLog);            sendBuildFail(midjourneyLog);            return;        }        String imgFile = lastLog.getImgFile();        File file = new File(imgFile);        if (!file.exists()) {            log.info("[失败提示] 上一次图片不存在 log:{}", lastLog);            sendBuildFail(midjourneyLog);            return;        }        cutAndSendImg(midjourneyLog, file);    }    private void cutAndSendImg(MidjourneyLog midjourneyLog, File oldFile) throws IOException {        BufferedImage bufferedImage = ImageIO.read(new FileInputStream(oldFile));        int width = bufferedImage.getWidth();        int height = bufferedImage.getHeight();        String msg = midjourneyLog.getPrompt().trim();        String newFileName;        if (msg.equalsIgnoreCase("u1")) {            newFileName = oldFile.getPath() + "_u1.png";            ImgUtil.cut(oldFile, FileUtil.file(newFileName),                    new Rectangle(0, 0, width / 2, height / 2)            );        } else if (msg.equalsIgnoreCase("u2")) {            newFileName = oldFile.getPath() + "_u2.png";            ImgUtil.cut(oldFile, FileUtil.file(newFileName),                    new Rectangle(width / 2, 0, width / 2, height / 2)            );        } else if (msg.equalsIgnoreCase("u3")) {            newFileName = oldFile.getPath() + "_u3.png";            ImgUtil.cut(oldFile, FileUtil.file(newFileName),                    new Rectangle(0, height / 2, width / 2, height / 2)            );        } else if (msg.equalsIgnoreCase("u4")) {            newFileName = oldFile.getPath() + "_u4.png";            ImgUtil.cut(oldFile, FileUtil.file(newFileName),                    new Rectangle(width / 2, height / 2, width / 2, height / 2)            );        } else {            sendBuildFail(midjourneyLog);            return;        }        String mediaId = weChatBiz.sendImg(newFileName, midjourneyLog.getChannel());        log.info("[mediaId] {}", mediaId);        if (Strings.isNotEmpty(mediaId)) {            String wxId = memberService.selectWxidById(midjourneyLog.getMemberId());            if (Strings.isEmpty(wxId)) {                sendBuildFail(midjourneyLog);                return;            }            midjourneyLogService.updateFinish(midjourneyLog);            weChatBiz.sendImgMsg(wxId, mediaId, midjourneyLog.getChannel());        }    }    private boolean isFailMsg(String channel, MidjourneyLog midjourneyLog) {        return Objects.isNull(midjourneyLog) || Strings.isEmpty(midjourneyLog.getPrompt()) || Strings.isEmpty(channel)                || Objects.isNull(midjourneyLog.getMemberId()) || checkNotRunMsg(midjourneyLog);    }    public boolean checkNotRunMsg(MidjourneyLog midjourneyLog) {        String prompt = midjourneyLog.getPrompt();        if (midjourneyLog.getType() == MedjourneyLogType.BIG_IMG.getCode()) {            return !(prompt.equalsIgnoreCase("u1") || prompt.equalsIgnoreCase("u2")                    || prompt.equalsIgnoreCase("u3") || prompt.equalsIgnoreCase("u4"));        } else if (midjourneyLog.getType() == MedjourneyLogType.ITERATIVE.getCode()) {            return !(prompt.equalsIgnoreCase("v1") || prompt.equalsIgnoreCase("v2")                    || prompt.equalsIgnoreCase("v3") || prompt.equalsIgnoreCase("v4"));        } else {            return false;        }    }    private String cleanMsg(String msg) {        msg = msg.replace("—", "--")                .replace("-- ", "--")                .replace("-- ", "--")                .replace("-- ", "--")                .replace(",", ",")                .replace("/", "")                .replace("--v", " --v ")                .replace("--niji", " --niji ")                .replace("--ar", " --ar ")                .replace("--aspect", " --ar ")                .replace("--chaos", " --chaos ")                .replace("--c", " --c ")                .replace("--no", " --no ")                .replace("--quality", " --quality ")                .replace("--q", " --q ")                .replace("--repeat", " --repeat ")                .replace("--s", " --s ")                .replace("--upbeta", " --upbeta ")                .trim();        if (TextUtil.isHaveChinese(msg)) {            msg = translateBiz.translate(msg);        }        msg = msg.replace(",", ",")                .replace("/", "")                .replace("--v5", " --v 5 ")                .replace("--niji5", " --niji 5 ")                .trim();        if (!msg.contains("--niji") && !msg.contains("--v") && msg.length() > 3) {            msg = msg + " --v 5";        }        return msg;    }    public void sendBuildFail(MidjourneyLog midjourneyLog) {        String wxId = memberService.selectWxidById(midjourneyLog.getMemberId());        CallBackUtil.failCallBack(midjourneyLog.getChannel(), MIDJOURNEY, midjourneyLog.getId());        midjourneyLogService.updateFail(midjourneyLog);        weChatBiz.sendTextMsg(wxId, "抱歉,您的消息【" + midjourneyLog.getPrompt() + "】处理失败,已为您退换对应电量",                midjourneyLog.getChannel());    }    @Scheduled(cron = "34 * * * * ? ")    public void checkImg() {        //房间号清理        cleanRoomNumber();        //查询所有进行中的任务        List logs = midjourneyLogService.selectAllDoing().stream()                .filter(log -> Objects.nonNull(log.getRoomId())).collect(Collectors.toList());        //找到超时任务进行关闭        List failLogs = logs.stream()                .filter(log -> System.currentTimeMillis() - log.getCreateTime().getTime() > MAX_WAIT_TIME)                .collect(Collectors.toList());        failLogs.forEach(this::sendBuildFail);        //剩余任务整理出来房间号        logs.removeAll(failLogs);        Set roomSet = logs.stream().map(MidjourneyLog::getRoomId).collect(Collectors.toSet());        //轮询当前进度        for (Integer roomId : roomSet) {            RoomInfo roomInfo = roomInfoService.findById(roomId);            if (Objects.isNull(roomInfo)) {                log.error("[room没找到] roomId:{} 没找到对应房间,看一下是不是挂了", roomId);                continue;            }            Discord discord = discordService.findById(roomInfo.getDiscordId());            List midMsgs = readNowList(roomInfo, discord);            log.info("[消息列表] {}", midMsgs);            checkAndSendMsg(midMsgs, logs, roomInfo);        }    }    private void cleanRoomNumber() {        List roomInfos = roomInfoService.findAll();        for (RoomInfo roomInfo : roomInfos) {            int count = midjourneyLogService.selectRoomOnUse(roomInfo.getId());            roomInfo.setNowNumber(count);            roomInfoService.update(roomInfo);        }    }    private void checkAndSendMsg(List midMsgs, List logs, RoomInfo roomInfo) {        List roomLogs = logs.stream()                .filter(log -> log.getRoomId().equals(roomInfo.getId()))                .collect(Collectors.toList());        midMsgs.stream()                .filter(this::isPrintOk)                .forEach(m -> roomLogs.forEach(midjourey -> {                    if (m.getContent().startsWith(MID_JOURNEY_HEAD + filterHead(midjourey.getPrompt()))) {                        log.info("[Midjourney 配对] msg:{}  key:{}", m, midjourey.getPrompt());                        String wxid = memberService.selectWxidById(midjourey.getMemberId());                        String url = m.getAttachments().get(0).getUrl();                        String localPath = Constant.FILE_PATH + UUID.randomUUID() + "." + FileUtil.getSuffix(url);                        ImgDownUtil.getImage(url.replace("https://", "Http://"), localPath);                        localPath = localPath.replace("\\", "/");                        if ("WEBp".equals(FileUtil.getSuffix(localPath))) {com.example.midjourney.util.ImgUtil.webpToPng(localPath, localPath + ".png");localPath = localPath + ".png";                        }                        String mediaId = weChatBiz.sendImg(localPath, midjourey.getChannel());                        log.info("[mediaId] {}", mediaId);                        if (Strings.isNotEmpty(mediaId)) {weChatBiz.sendImgMsg(wxid, mediaId, midjourey.getChannel());weChatBiz.sendTextMsg(wxid, "下载高清大图口令:\n\n☆左上图回复:U1\n\n☆右上图回复:U2" +        "\n\n☆左下图回复:U3\n\n☆右下图回复:U4\n\n下载大图也会扣电量哦~", midjourey.getChannel());midjourey.setImgFile(localPath);midjourneyLogService.updateFinish(midjourey);                        }                    }                }));    }    private String filterHead(String prompt) {        return Lists.newArrayList(Splitter.on("--").split(prompt)).stream().findFirst().orElse("").trim();    }    private boolean isPrintOk(MidMsg midMsg) {        if (Objects.isNull(midMsg)                || Strings.isEmpty(midMsg.getContent())                || !midMsg.getContent().contains(MID_FIND_LEFT)) {            return false;        }        String str = midMsg.getContent().substring(midMsg.getContent().indexOf(MID_FIND_LEFT));        return !str.contains("%") && !str.contains("Waiting");    }    private boolean isBlackWord(String msg) {        msg = msg.toLowerCase();        for (String s : Constant.MidjourneyBlackWord) {            if (msg.contains(s.toLowerCase())) {                return true;            }        }        return false;    }    private void sendSensitive(MidjourneyLog midjourneyLog) {        String wxid = memberService.selectWxidById(midjourneyLog.getMemberId());        midjourneyLogService.updateSensitive(midjourneyLog);        weChatBiz.sendTextMsg(wxid, "【违规提示】输入内容包含违禁词,依法进行屏蔽。", midjourneyLog.getChannel());    }        private void sendSensitive(MidjourneyLog midjourneyLog, BaiduTextCensor censor) {        String reason = Joiner.on("、")                .join(Safes.of(censor.getData()).stream()                        .map(BaiduTextCensorData::getMsg)                        .collect(Collectors.toList()));        reason = Strings.isEmpty(reason) ? "包含敏感信息" : reason;        String fullText = "【违规提示】输入内容因 " + reason + " ,依法进行屏蔽。(百度提供审核能力)";        String wxid = memberService.selectWxidById(midjourneyLog.getMemberId());        midjourneyLogService.updateSensitive(midjourneyLog);        weChatBiz.sendTextMsg(wxid, fullText, midjourneyLog.getChannel());    }    public List readNowList(RoomInfo roomInfo, Discord discord) {        OkHttpClient client = new OkHttpClient().newBuilder()                .build();        Request request = new Request.Builder()                .url("https://discord.com/api/v9/channels/" + roomInfo.getDiscordChannelId() + "/messages?limit=50")                .get()                .addHeader("authority", "discord.com")                .addHeader("accept", "**")                .addHeader("accept-language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6")                .addHeader("authorization", discord.getAuthorization())                .addHeader("cache-control", "no-cache")                .addHeader("cookie", discord.getCookie())                .addHeader("origin", "https://discord.com")                .addHeader("pragma", "no-cache")                .addHeader("referer", "https://discord.com/channels/" + roomInfo.getDiscordGuildId() +                        "/" + roomInfo.getDiscordChannelId())                .addHeader("sec-ch-ua", "\"Not_A Brand\";v=\"99\", \"Microsoft Edge\";v=\"109\", \"Chromium\";v=\"109\"")                .addHeader("sec-ch-ua-mobile", "?0")                .addHeader("sec-ch-ua-platfORM", "\"windows\"")                .addHeader("sec-fetch-dest", "empty")                .addHeader("sec-fetch-mode", "cors")                .addHeader("sec-fetch-site", "same-origin")                .addHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.70")                .addHeader("x-debug-options", "bugReporterEnabled")                .addHeader("x-discord-locale", "zh-CN")                .addHeader("x-super-properties", discord.getSuperProperties())                .build();        Response response = client.newCall(request).execute();        if (response.code() == 204) {            roomInfoService.addOnce(roomInfo);            response.close();            return true;        }        response.close();        log.info("[midjourney发消息失败] {}", response.code());        return false;    }}

MidjourneyLogService

package com.example.midjourney.service;import com.example.midjourney.bean.pojo.MidjourneyLog;import com.example.midjourney.bean.pojo.RoomInfo;import java.util.List;public interface MidjourneyLogService {    MidjourneyLog findById(Long id);    MidjourneyLog findLastNormalLog(Integer memberId);    MidjourneyLog findLastNormalOrIterative(Integer memberId);    void updateRoom(MidjourneyLog midjourneyLog, RoomInfo roomInfo);    List selectAllDoing();    void updateFail(MidjourneyLog midjourneyLog);    void updateSensitive(MidjourneyLog midjourneyLog);    void updatePrompt(MidjourneyLog midjourneyLog, String prompt);    void updateFinish(MidjourneyLog midjourey);    int selectRoomOnUse(Integer id);}

MidjourneyLogServiceImpl

package com.example.midjourney.service.impl;import com.example.midjourney.bean.pojo.MidjourneyLog;import com.example.midjourney.bean.pojo.RoomInfo;import com.example.midjourney.mapper.MidjourneyLogMapper;import com.example.midjourney.service.MidjourneyLogService;import com.example.midjourney.util.Safes;import org.springframework.stereotype.Service;import javax.annotation.Resource;import java.util.List;@Servicepublic class MidjourneyLogServiceImpl implements MidjourneyLogService {    @Resource    private MidjourneyLogMapper midjourneyLogMapper;    @Override    public MidjourneyLog findById(Long id) {        return midjourneyLogMapper.selectByPrimaryKey(id);    }    @Override    public MidjourneyLog findLastNormalLog(Integer memberId) {        return midjourneyLogMapper.findLastNormalLog(memberId);    }    @Override    public MidjourneyLog findLastNormalOrIterative(Integer memberId) {        return midjourneyLogMapper.findLastNormalOrIterative(memberId);    }    @Override    public void updateRoom(MidjourneyLog midjourneyLog, RoomInfo roomInfo) {        midjourneyLog.setRoomId(roomInfo.getId());        midjourneyLogMapper.updateByPrimaryKeySelective(midjourneyLog);    }    @Override    public List selectAllDoing() {        MidjourneyLog midjourneyLog = new MidjourneyLog();        midjourneyLog.setStatus(0);        return Safes.of(midjourneyLogMapper.select(midjourneyLog));    }    @Override    public void updateFail(MidjourneyLog midjourneyLog) {        midjourneyLog.setStatus(500);        midjourneyLogMapper.updateByPrimaryKeySelective(midjourneyLog);    }    @Override    public void updateSensitive(MidjourneyLog midjourneyLog) {        midjourneyLog.setStatus(-1);        midjourneyLogMapper.updateByPrimaryKeySelective(midjourneyLog);    }    @Override    public void updatePrompt(MidjourneyLog midjourneyLog, String prompt) {        midjourneyLog.setPrompt(prompt);        midjourneyLogMapper.updateByPrimaryKeySelective(midjourneyLog);    }    @Override    public void updateFinish(MidjourneyLog midjourey) {        midjourey.setStatus(1);        midjourneyLogMapper.updateByPrimaryKeySelective(midjourey);    }    @Override    public int selectRoomOnUse(Integer id) {        return midjourneyLogMapper.selectRoomOnUse(id);    }}

MidjourneyLogMapper 

这里用了通用mapper插件

package com.example.midjourney.mapper;import com.example.midjourney.bean.pojo.MidjourneyLog;import org.apache.ibatis.annotations.Param;import org.apache.ibatis.annotations.Select;import tk.mybatis.mapper.common.Mapper;public interface MidjourneyLogMapper extends Mapper {    String COLUMN = " id, member_id AS memberId, prompt, channel, type, " +            "status, img_file AS imgFile, room_id AS roomId ";    @Select("SELECT " + COLUMN + " FROM midjourney_log WHERE member_id = #{memberId} AND type = 0 ORDER BY id DESC LIMIT 1")    MidjourneyLog findLastNormalLog(@Param("memberId") Integer memberId);    @Select("SELECT " + COLUMN + " FROM midjourney_log WHERE member_id = #{memberId} AND (type = 0 OR type = 2)" +            " ORDER BY id DESC LIMIT 1")    MidjourneyLog findLastNormalOrIterative(Integer memberId);    @Select("SELECT COUNT(*) AS count FROM midjourney_log WHERE room_id = #{id} AND status = 0")    Integer selectRoomOnUse(@Param("id") Integer id);}

Constant

常量类可以根据需要自定调整敏感词

敏感词写上审核不过,大家自己想想脑补下吧,各种不好的词都往那个敏感词List里面自己加吧!

package com.example.midjourney.contant;import com.example.midjourney.bean.UserInfo;import com.example.midjourney.biz.WeChatBiz;import com.example.midjourney.util.BaiduUtil;import com.Google.common.collect.Lists;import com.google.common.collect.Maps;import java.util.List;import java.util.Map;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Constant {    public static ExecutorService threadPool = Executors.newFixedThreadPool(20);    public static Map imgWordUserMap = Maps.newConcurrentMap();    public static String MID_JOURNEY_HEAD = "**";    public static String MID_FIND_LEFT = "** - <";    public static String FILE_PATH = "D:\\000img\\";    public static String CHATGPT = "chatgpt";    public static String MIDJOURNEY = "midjourney";    public static final String INSUFFICIENT = "insufficient_quota";    public static Map WX_TOKEN_CHANNEL = Maps.newHashMap();    public static Map WX_TOKEN_MAP = Maps.newHashMap();    public static String BAIDU_TOKEN = BaiduUtil.queryBaiduToken();    public static Map CHANNEL_CALL_BACK = Maps.newHashMap();        public static long MAX_WAIT_TIME = 1000 * 60 * 15;    public static List MidjourneyBlackWord = Lists.newArrayList("写了审核不过,大家自己发挥想象吧...");}

来源地址:https://blog.csdn.net/qq_20051535/article/details/131209100

--结束END--

本文标题: Java调用Midjourney进行AI画图原生版抓包实现支持中文

本文链接: https://lsjlt.com/news/411896.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作