mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-19 17:56:39 +08:00
feat: add blend and swapface task implements for midjourney
This commit is contained in:
parent
d772bbebe6
commit
24906a6df1
@ -9,6 +9,8 @@ func (t TaskType) String() string {
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
TaskImage = TaskType("image")
|
TaskImage = TaskType("image")
|
||||||
|
TaskBlend = TaskType("blend")
|
||||||
|
TaskSwapFace = TaskType("swapFace")
|
||||||
TaskUpscale = TaskType("upscale")
|
TaskUpscale = TaskType("upscale")
|
||||||
TaskVariation = TaskType("variation")
|
TaskVariation = TaskType("variation")
|
||||||
)
|
)
|
||||||
@ -16,6 +18,8 @@ const (
|
|||||||
// MjTask MidJourney 任务
|
// MjTask MidJourney 任务
|
||||||
type MjTask struct {
|
type MjTask struct {
|
||||||
Id int `json:"id"`
|
Id int `json:"id"`
|
||||||
|
TaskId string `json:"task_id"`
|
||||||
|
ImgArr []string `json:"img_arr"`
|
||||||
ChannelId string `json:"channel_id"`
|
ChannelId string `json:"channel_id"`
|
||||||
SessionId string `json:"session_id"`
|
SessionId string `json:"session_id"`
|
||||||
Type TaskType `json:"type"`
|
Type TaskType `json:"type"`
|
||||||
|
@ -87,6 +87,7 @@ func (h *MidJourneyHandler) Client(c *gin.Context) {
|
|||||||
func (h *MidJourneyHandler) Image(c *gin.Context) {
|
func (h *MidJourneyHandler) Image(c *gin.Context) {
|
||||||
var data struct {
|
var data struct {
|
||||||
SessionId string `json:"session_id"`
|
SessionId string `json:"session_id"`
|
||||||
|
TaskType string `json:"task_type"`
|
||||||
Prompt string `json:"prompt"`
|
Prompt string `json:"prompt"`
|
||||||
NegPrompt string `json:"neg_prompt"`
|
NegPrompt string `json:"neg_prompt"`
|
||||||
Rate string `json:"rate"`
|
Rate string `json:"rate"`
|
||||||
@ -95,7 +96,7 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
|||||||
Raw bool `json:"raw"`
|
Raw bool `json:"raw"`
|
||||||
Seed int64 `json:"seed"`
|
Seed int64 `json:"seed"`
|
||||||
Stylize int `json:"stylize"`
|
Stylize int `json:"stylize"`
|
||||||
Img string `json:"img"`
|
ImgArr []string `json:"img_arr"`
|
||||||
Tile bool `json:"tile"`
|
Tile bool `json:"tile"`
|
||||||
Quality float32 `json:"quality"`
|
Quality float32 `json:"quality"`
|
||||||
Weight float32 `json:"weight"`
|
Weight float32 `json:"weight"`
|
||||||
@ -121,12 +122,9 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
|||||||
if data.Chaos > 0 && !strings.Contains(prompt, "--c") && !strings.Contains(prompt, "--chaos") {
|
if data.Chaos > 0 && !strings.Contains(prompt, "--c") && !strings.Contains(prompt, "--chaos") {
|
||||||
prompt += fmt.Sprintf(" --c %d", data.Chaos)
|
prompt += fmt.Sprintf(" --c %d", data.Chaos)
|
||||||
}
|
}
|
||||||
if data.Img != "" {
|
|
||||||
prompt = fmt.Sprintf("%s %s", data.Img, prompt)
|
|
||||||
if data.Weight > 0 {
|
if data.Weight > 0 {
|
||||||
prompt += fmt.Sprintf(" --iw %f", data.Weight)
|
prompt += fmt.Sprintf(" --iw %f", data.Weight)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if data.Raw {
|
if data.Raw {
|
||||||
prompt += " --style raw"
|
prompt += " --style raw"
|
||||||
}
|
}
|
||||||
@ -152,7 +150,7 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
job := model.MidJourneyJob{
|
job := model.MidJourneyJob{
|
||||||
Type: types.TaskImage.String(),
|
Type: data.TaskType,
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
TaskId: taskId,
|
TaskId: taskId,
|
||||||
Progress: 0,
|
Progress: 0,
|
||||||
@ -166,10 +164,12 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
|||||||
|
|
||||||
h.pool.PushTask(types.MjTask{
|
h.pool.PushTask(types.MjTask{
|
||||||
Id: int(job.Id),
|
Id: int(job.Id),
|
||||||
|
TaskId: taskId,
|
||||||
SessionId: data.SessionId,
|
SessionId: data.SessionId,
|
||||||
Type: types.TaskImage,
|
Type: types.TaskImage,
|
||||||
Prompt: fmt.Sprintf("%s %s", taskId, prompt),
|
Prompt: prompt,
|
||||||
UserId: userId,
|
UserId: userId,
|
||||||
|
ImgArr: data.ImgArr,
|
||||||
})
|
})
|
||||||
|
|
||||||
client := h.pool.Clients.Get(uint(job.UserId))
|
client := h.pool.Clients.Get(uint(job.UserId))
|
||||||
|
@ -2,6 +2,7 @@ package mj
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"chatplus/core/types"
|
"chatplus/core/types"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ func NewClient(config types.MidJourneyConfig, proxy string, imgCdnURL string) *C
|
|||||||
return &Client{client: client, Config: config, apiURL: apiURL, imgCdnURL: imgCdnURL}
|
return &Client{client: client, Config: config, apiURL: apiURL, imgCdnURL: imgCdnURL}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Imagine(prompt string) error {
|
func (c *Client) Imagine(task types.MjTask) error {
|
||||||
interactionsReq := &InteractionsRequest{
|
interactionsReq := &InteractionsRequest{
|
||||||
Type: 2,
|
Type: 2,
|
||||||
ApplicationID: ApplicationID,
|
ApplicationID: ApplicationID,
|
||||||
@ -49,7 +50,7 @@ func (c *Client) Imagine(prompt string) error {
|
|||||||
{
|
{
|
||||||
"type": 3,
|
"type": 3,
|
||||||
"name": "prompt",
|
"name": "prompt",
|
||||||
"value": prompt,
|
"value": fmt.Sprintf("%s %s", task.TaskId, task.Prompt),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"application_command": map[string]any{
|
"application_command": map[string]any{
|
||||||
@ -88,20 +89,28 @@ func (c *Client) Imagine(prompt string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) Blend(task types.MjTask) error {
|
||||||
|
return errors.New("function not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) SwapFace(task types.MjTask) error {
|
||||||
|
return errors.New("function not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
// Upscale 放大指定的图片
|
// Upscale 放大指定的图片
|
||||||
func (c *Client) Upscale(index int, messageId string, hash string) error {
|
func (c *Client) Upscale(task types.MjTask) error {
|
||||||
flags := 0
|
flags := 0
|
||||||
interactionsReq := &InteractionsRequest{
|
interactionsReq := &InteractionsRequest{
|
||||||
Type: 3,
|
Type: 3,
|
||||||
ApplicationID: ApplicationID,
|
ApplicationID: ApplicationID,
|
||||||
GuildID: c.Config.GuildId,
|
GuildID: c.Config.GuildId,
|
||||||
ChannelID: c.Config.ChanelId,
|
ChannelID: c.Config.ChanelId,
|
||||||
MessageFlags: &flags,
|
MessageFlags: flags,
|
||||||
MessageID: &messageId,
|
MessageID: task.MessageId,
|
||||||
SessionID: SessionID,
|
SessionID: SessionID,
|
||||||
Data: map[string]any{
|
Data: map[string]any{
|
||||||
"component_type": 2,
|
"component_type": 2,
|
||||||
"custom_id": fmt.Sprintf("MJ::JOB::upsample::%d::%s", index, hash),
|
"custom_id": fmt.Sprintf("MJ::JOB::upsample::%d::%s", task.Index, task.MessageHash),
|
||||||
},
|
},
|
||||||
Nonce: fmt.Sprintf("%d", time.Now().UnixNano()),
|
Nonce: fmt.Sprintf("%d", time.Now().UnixNano()),
|
||||||
}
|
}
|
||||||
@ -120,19 +129,19 @@ func (c *Client) Upscale(index int, messageId string, hash string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Variation 以指定的图片的视角进行变换再创作,注意需要在对应的频道中关闭 Remix 变换,否则 Variation 指令将不会生效
|
// Variation 以指定的图片的视角进行变换再创作,注意需要在对应的频道中关闭 Remix 变换,否则 Variation 指令将不会生效
|
||||||
func (c *Client) Variation(index int, messageId string, hash string) error {
|
func (c *Client) Variation(task types.MjTask) error {
|
||||||
flags := 0
|
flags := 0
|
||||||
interactionsReq := &InteractionsRequest{
|
interactionsReq := &InteractionsRequest{
|
||||||
Type: 3,
|
Type: 3,
|
||||||
ApplicationID: ApplicationID,
|
ApplicationID: ApplicationID,
|
||||||
GuildID: c.Config.GuildId,
|
GuildID: c.Config.GuildId,
|
||||||
ChannelID: c.Config.ChanelId,
|
ChannelID: c.Config.ChanelId,
|
||||||
MessageFlags: &flags,
|
MessageFlags: flags,
|
||||||
MessageID: &messageId,
|
MessageID: task.MessageId,
|
||||||
SessionID: SessionID,
|
SessionID: SessionID,
|
||||||
Data: map[string]any{
|
Data: map[string]any{
|
||||||
"component_type": 2,
|
"component_type": 2,
|
||||||
"custom_id": fmt.Sprintf("MJ::JOB::variation::%d::%s", index, hash),
|
"custom_id": fmt.Sprintf("MJ::JOB::variation::%d::%s", task.Index, task.MessageHash),
|
||||||
},
|
},
|
||||||
Nonce: fmt.Sprintf("%d", time.Now().UnixNano()),
|
Nonce: fmt.Sprintf("%d", time.Now().UnixNano()),
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,11 @@ package plus
|
|||||||
import (
|
import (
|
||||||
"chatplus/core/types"
|
"chatplus/core/types"
|
||||||
logger2 "chatplus/logger"
|
logger2 "chatplus/logger"
|
||||||
|
"chatplus/utils"
|
||||||
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/imroc/req/v3"
|
"github.com/imroc/req/v3"
|
||||||
@ -23,8 +26,9 @@ func NewClient(config types.MidJourneyPlusConfig) *Client {
|
|||||||
|
|
||||||
type ImageReq struct {
|
type ImageReq struct {
|
||||||
BotType string `json:"botType"`
|
BotType string `json:"botType"`
|
||||||
Prompt string `json:"prompt"`
|
Prompt string `json:"prompt,omitempty"`
|
||||||
Base64Array []interface{} `json:"base64Array,omitempty"`
|
Dimensions string `json:"dimensions,omitempty"`
|
||||||
|
Base64Array []string `json:"base64Array,omitempty"`
|
||||||
AccountFilter struct {
|
AccountFilter struct {
|
||||||
InstanceId string `json:"instanceId"`
|
InstanceId string `json:"instanceId"`
|
||||||
Modes []interface{} `json:"modes"`
|
Modes []interface{} `json:"modes"`
|
||||||
@ -49,12 +53,114 @@ type ErrRes struct {
|
|||||||
} `json:"error"`
|
} `json:"error"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Imagine(prompt string) (ImageRes, error) {
|
func (c *Client) Imagine(task types.MjTask) (ImageRes, error) {
|
||||||
apiURL := fmt.Sprintf("%s/mj-fast/mj/submit/imagine", c.Config.ApiURL)
|
apiURL := fmt.Sprintf("%s/mj-fast/mj/submit/imagine", c.Config.ApiURL)
|
||||||
body := ImageReq{
|
body := ImageReq{
|
||||||
BotType: "MID_JOURNEY",
|
BotType: "MID_JOURNEY",
|
||||||
Prompt: prompt,
|
Prompt: task.Prompt,
|
||||||
NotifyHook: c.Config.NotifyURL,
|
NotifyHook: c.Config.NotifyURL,
|
||||||
|
Base64Array: make([]string, 1),
|
||||||
|
}
|
||||||
|
// 生成图片 Base64 编码
|
||||||
|
if len(task.ImgArr) > 0 {
|
||||||
|
imageData, err := utils.DownloadImage(task.ImgArr[0], "")
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("error with download image: ", err)
|
||||||
|
} else {
|
||||||
|
body.Base64Array[0] = "data:image/png;base64," + base64.StdEncoding.EncodeToString(imageData)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
var res ImageRes
|
||||||
|
var errRes ErrRes
|
||||||
|
r, err := req.C().R().
|
||||||
|
SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
||||||
|
SetBody(body).
|
||||||
|
SetSuccessResult(&res).
|
||||||
|
SetErrorResult(&errRes).
|
||||||
|
Post(apiURL)
|
||||||
|
if err != nil {
|
||||||
|
errStr, _ := io.ReadAll(r.Body)
|
||||||
|
return ImageRes{}, fmt.Errorf("请求 API 出错:%v,%v", err, string(errStr))
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.IsErrorState() {
|
||||||
|
return ImageRes{}, fmt.Errorf("API 返回错误:%s", errRes.Error.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blend 融图
|
||||||
|
func (c *Client) Blend(task types.MjTask) (ImageRes, error) {
|
||||||
|
apiURL := fmt.Sprintf("%s/mj-fast/mj/submit/blend", c.Config.ApiURL)
|
||||||
|
body := ImageReq{
|
||||||
|
BotType: "MID_JOURNEY",
|
||||||
|
Dimensions: "SQUARE",
|
||||||
|
NotifyHook: c.Config.NotifyURL,
|
||||||
|
Base64Array: make([]string, 1),
|
||||||
|
}
|
||||||
|
// 生成图片 Base64 编码
|
||||||
|
if len(task.ImgArr) > 0 {
|
||||||
|
for _, imgURL := range task.ImgArr {
|
||||||
|
imageData, err := utils.DownloadImage(imgURL, "")
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("error with download image: ", err)
|
||||||
|
} else {
|
||||||
|
body.Base64Array[0] = "data:image/png;base64," + base64.StdEncoding.EncodeToString(imageData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var res ImageRes
|
||||||
|
var errRes ErrRes
|
||||||
|
r, err := req.C().R().
|
||||||
|
SetHeader("Authorization", "Bearer "+c.Config.ApiKey).
|
||||||
|
SetBody(body).
|
||||||
|
SetSuccessResult(&res).
|
||||||
|
SetErrorResult(&errRes).
|
||||||
|
Post(apiURL)
|
||||||
|
if err != nil {
|
||||||
|
errStr, _ := io.ReadAll(r.Body)
|
||||||
|
return ImageRes{}, fmt.Errorf("请求 API 出错:%v,%v", err, string(errStr))
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.IsErrorState() {
|
||||||
|
return ImageRes{}, fmt.Errorf("API 返回错误:%s", errRes.Error.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwapFace 换脸
|
||||||
|
func (c *Client) SwapFace(task types.MjTask) (ImageRes, error) {
|
||||||
|
apiURL := fmt.Sprintf("%s/mj-fast/mj/insight-face/swap", c.Config.ApiURL)
|
||||||
|
// 生成图片 Base64 编码
|
||||||
|
if len(task.ImgArr) != 2 {
|
||||||
|
return ImageRes{}, errors.New("参数错误,必须上传2张图片")
|
||||||
|
}
|
||||||
|
var sourceBase64 string
|
||||||
|
var targetBase64 string
|
||||||
|
imageData, err := utils.DownloadImage(task.ImgArr[0], "")
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("error with download image: ", err)
|
||||||
|
} else {
|
||||||
|
sourceBase64 = "data:image/png;base64," + base64.StdEncoding.EncodeToString(imageData)
|
||||||
|
}
|
||||||
|
imageData, err = utils.DownloadImage(task.ImgArr[1], "")
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("error with download image: ", err)
|
||||||
|
} else {
|
||||||
|
targetBase64 = "data:image/png;base64," + base64.StdEncoding.EncodeToString(imageData)
|
||||||
|
}
|
||||||
|
|
||||||
|
body := gin.H{
|
||||||
|
"sourceBase64": sourceBase64,
|
||||||
|
"targetBase64": targetBase64,
|
||||||
|
"accountFilter": gin.H{
|
||||||
|
"instanceId": "",
|
||||||
|
},
|
||||||
|
"notifyHook": c.Config.NotifyURL,
|
||||||
|
"state": "",
|
||||||
}
|
}
|
||||||
var res ImageRes
|
var res ImageRes
|
||||||
var errRes ErrRes
|
var errRes ErrRes
|
||||||
@ -77,10 +183,10 @@ func (c *Client) Imagine(prompt string) (ImageRes, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Upscale 放大指定的图片
|
// Upscale 放大指定的图片
|
||||||
func (c *Client) Upscale(index int, messageId string, hash string) (ImageRes, error) {
|
func (c *Client) Upscale(task types.MjTask) (ImageRes, error) {
|
||||||
body := map[string]string{
|
body := map[string]string{
|
||||||
"customId": fmt.Sprintf("MJ::JOB::upsample::%d::%s", index, hash),
|
"customId": fmt.Sprintf("MJ::JOB::upsample::%d::%s", task.Index, task.MessageHash),
|
||||||
"taskId": messageId,
|
"taskId": task.MessageId,
|
||||||
"notifyHook": c.Config.NotifyURL,
|
"notifyHook": c.Config.NotifyURL,
|
||||||
}
|
}
|
||||||
apiURL := fmt.Sprintf("%s/mj/submit/action", c.Config.ApiURL)
|
apiURL := fmt.Sprintf("%s/mj/submit/action", c.Config.ApiURL)
|
||||||
@ -104,10 +210,10 @@ func (c *Client) Upscale(index int, messageId string, hash string) (ImageRes, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Variation 以指定的图片的视角进行变换再创作,注意需要在对应的频道中关闭 Remix 变换,否则 Variation 指令将不会生效
|
// Variation 以指定的图片的视角进行变换再创作,注意需要在对应的频道中关闭 Remix 变换,否则 Variation 指令将不会生效
|
||||||
func (c *Client) Variation(index int, messageId string, hash string) (ImageRes, error) {
|
func (c *Client) Variation(task types.MjTask) (ImageRes, error) {
|
||||||
body := map[string]string{
|
body := map[string]string{
|
||||||
"customId": fmt.Sprintf("MJ::JOB::variation::%d::%s", index, hash),
|
"customId": fmt.Sprintf("MJ::JOB::variation::%d::%s", task.Index, task.MessageHash),
|
||||||
"taskId": messageId,
|
"taskId": task.MessageId,
|
||||||
"notifyHook": c.Config.NotifyURL,
|
"notifyHook": c.Config.NotifyURL,
|
||||||
}
|
}
|
||||||
apiURL := fmt.Sprintf("%s/mj/submit/action", c.Config.ApiURL)
|
apiURL := fmt.Sprintf("%s/mj/submit/action", c.Config.ApiURL)
|
||||||
|
@ -69,18 +69,24 @@ func (s *Service) Run() {
|
|||||||
var res ImageRes
|
var res ImageRes
|
||||||
switch task.Type {
|
switch task.Type {
|
||||||
case types.TaskImage:
|
case types.TaskImage:
|
||||||
index := strings.Index(task.Prompt, " ")
|
res, err = s.Client.Imagine(task)
|
||||||
res, err = s.Client.Imagine(task.Prompt[index+1:])
|
|
||||||
break
|
break
|
||||||
case types.TaskUpscale:
|
case types.TaskUpscale:
|
||||||
res, err = s.Client.Upscale(task.Index, task.MessageId, task.MessageHash)
|
res, err = s.Client.Upscale(task)
|
||||||
break
|
break
|
||||||
case types.TaskVariation:
|
case types.TaskVariation:
|
||||||
res, err = s.Client.Variation(task.Index, task.MessageId, task.MessageHash)
|
res, err = s.Client.Variation(task)
|
||||||
|
break
|
||||||
|
case types.TaskBlend:
|
||||||
|
res, err = s.Client.Blend(task)
|
||||||
|
break
|
||||||
|
case types.TaskSwapFace:
|
||||||
|
res, err = s.Client.SwapFace(task)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil || (res.Code != 1 && res.Code != 22) {
|
if err != nil || (res.Code != 1 && res.Code != 22) {
|
||||||
logger.Error("绘画任务执行失败:", err)
|
logger.Error("绘画任务执行失败:", err, res.Description)
|
||||||
// update the task progress
|
// update the task progress
|
||||||
s.db.Model(&model.MidJourneyJob{Id: uint(task.Id)}).UpdateColumn("progress", -1)
|
s.db.Model(&model.MidJourneyJob{Id: uint(task.Id)}).UpdateColumn("progress", -1)
|
||||||
// 任务失败,通知前端
|
// 任务失败,通知前端
|
||||||
|
@ -65,14 +65,20 @@ func (s *Service) Run() {
|
|||||||
logger.Infof("%s handle a new MidJourney task: %+v", s.name, task)
|
logger.Infof("%s handle a new MidJourney task: %+v", s.name, task)
|
||||||
switch task.Type {
|
switch task.Type {
|
||||||
case types.TaskImage:
|
case types.TaskImage:
|
||||||
err = s.client.Imagine(task.Prompt)
|
err = s.client.Imagine(task)
|
||||||
break
|
break
|
||||||
case types.TaskUpscale:
|
case types.TaskUpscale:
|
||||||
err = s.client.Upscale(task.Index, task.MessageId, task.MessageHash)
|
err = s.client.Upscale(task)
|
||||||
|
|
||||||
break
|
break
|
||||||
case types.TaskVariation:
|
case types.TaskVariation:
|
||||||
err = s.client.Variation(task.Index, task.MessageId, task.MessageHash)
|
err = s.client.Variation(task)
|
||||||
|
break
|
||||||
|
case types.TaskBlend:
|
||||||
|
err = s.client.Blend(task)
|
||||||
|
break
|
||||||
|
case types.TaskSwapFace:
|
||||||
|
err = s.client.SwapFace(task)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -8,8 +8,8 @@ const (
|
|||||||
type InteractionsRequest struct {
|
type InteractionsRequest struct {
|
||||||
Type int `json:"type"`
|
Type int `json:"type"`
|
||||||
ApplicationID string `json:"application_id"`
|
ApplicationID string `json:"application_id"`
|
||||||
MessageFlags *int `json:"message_flags,omitempty"`
|
MessageFlags int `json:"message_flags,omitempty"`
|
||||||
MessageID *string `json:"message_id,omitempty"`
|
MessageID string `json:"message_id,omitempty"`
|
||||||
GuildID string `json:"guild_id"`
|
GuildID string `json:"guild_id"`
|
||||||
ChannelID string `json:"channel_id"`
|
ChannelID string `json:"channel_id"`
|
||||||
SessionID string `json:"session_id"`
|
SessionID string `json:"session_id"`
|
||||||
|
@ -48,12 +48,12 @@ func DownloadImage(imageURL string, proxy string) ([]byte, error) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest("GET", imageURL, nil)
|
request, err := http.NewRequest("GET", imageURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -201,9 +201,6 @@
|
|||||||
.page-mj .inner .task-list-box .task-list-inner .title-tabs .el-tabs__active-bar {
|
.page-mj .inner .task-list-box .task-list-inner .title-tabs .el-tabs__active-bar {
|
||||||
background-color: #47fff1;
|
background-color: #47fff1;
|
||||||
}
|
}
|
||||||
.page-mj .inner .task-list-box .task-list-inner .title-tabs .el-tabs__content {
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
.page-mj .inner .task-list-box .task-list-inner .el-textarea {
|
.page-mj .inner .task-list-box .task-list-inner .el-textarea {
|
||||||
--el-input-focus-border-color: #47fff1;
|
--el-input-focus-border-color: #47fff1;
|
||||||
}
|
}
|
||||||
@ -254,6 +251,12 @@
|
|||||||
height: 120px;
|
height: 120px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
.page-mj .inner .task-list-box .task-list-inner .img-inline {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.page-mj .inner .task-list-box .task-list-inner .img-inline .img-uploader {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
.page-mj .inner .task-list-box .task-list-inner .submit-btn {
|
.page-mj .inner .task-list-box .task-list-inner .submit-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
|
@ -86,9 +86,6 @@
|
|||||||
.page-sd .inner .task-list-box .task-list-inner .title-tabs .el-tabs__active-bar {
|
.page-sd .inner .task-list-box .task-list-inner .title-tabs .el-tabs__active-bar {
|
||||||
background-color: #47fff1;
|
background-color: #47fff1;
|
||||||
}
|
}
|
||||||
.page-sd .inner .task-list-box .task-list-inner .title-tabs .el-tabs__content {
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
.page-sd .inner .task-list-box .task-list-inner .el-textarea {
|
.page-sd .inner .task-list-box .task-list-inner .el-textarea {
|
||||||
--el-input-focus-border-color: #47fff1;
|
--el-input-focus-border-color: #47fff1;
|
||||||
}
|
}
|
||||||
@ -139,6 +136,12 @@
|
|||||||
height: 120px;
|
height: 120px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
.page-sd .inner .task-list-box .task-list-inner .img-inline {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.page-sd .inner .task-list-box .task-list-inner .img-inline .img-uploader {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
.page-sd .inner .task-list-box .task-list-inner .submit-btn {
|
.page-sd .inner .task-list-box .task-list-inner .submit-btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
|
4
web/src/assets/css/mobile/image-mj.css
Normal file
4
web/src/assets/css/mobile/image-mj.css
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.mobile-mj .content .van-field__label {
|
||||||
|
width: 100px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
8
web/src/assets/css/mobile/image-mj.styl
Normal file
8
web/src/assets/css/mobile/image-mj.styl
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.mobile-mj {
|
||||||
|
.content {
|
||||||
|
.van-field__label {
|
||||||
|
width 100px
|
||||||
|
text-align right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,10 +23,6 @@
|
|||||||
background-color: #47FFF1;
|
background-color: #47FFF1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-tabs .el-tabs__content {
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-textarea {
|
.el-textarea {
|
||||||
--el-input-focus-border-color: #47FFF1;
|
--el-input-focus-border-color: #47FFF1;
|
||||||
}
|
}
|
||||||
@ -90,6 +86,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.img-inline {
|
||||||
|
display flex
|
||||||
|
|
||||||
|
.img-uploader {
|
||||||
|
margin-right 10px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.submit-btn {
|
.submit-btn {
|
||||||
display flex
|
display flex
|
||||||
margin: 20px 0
|
margin: 20px 0
|
||||||
@ -192,7 +196,7 @@
|
|||||||
top 10px
|
top 10px
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover{
|
&:hover {
|
||||||
.remove {
|
.remove {
|
||||||
display block
|
display block
|
||||||
}
|
}
|
||||||
|
@ -167,8 +167,8 @@
|
|||||||
<div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
|
<div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
|
||||||
<h2>AI绘画</h2>
|
<h2>AI绘画</h2>
|
||||||
<el-form>
|
<el-form>
|
||||||
<el-tabs v-model="activeName" class="title-tabs">
|
<el-tabs v-model="activeName" class="title-tabs" @tabChange="tabChange">
|
||||||
<el-tab-pane label="图生图(可选)" name="图生图">
|
<el-tab-pane label="文生图(可选)" name="image">
|
||||||
<div class="text">图生图:以某张图片为底稿参考来创作绘画,生成类似风格或类型图像,支持 PNG 和 JPG 格式图片;
|
<div class="text">图生图:以某张图片为底稿参考来创作绘画,生成类似风格或类型图像,支持 PNG 和 JPG 格式图片;
|
||||||
</div>
|
</div>
|
||||||
<div class="param-line pt">
|
<div class="param-line pt">
|
||||||
@ -194,7 +194,7 @@
|
|||||||
|
|
||||||
<div class="param-line">
|
<div class="param-line">
|
||||||
<el-upload class="img-uploader" :auto-upload="true" :show-file-list="false"
|
<el-upload class="img-uploader" :auto-upload="true" :show-file-list="false"
|
||||||
:http-request="afterRead" style="--el-color-primary:#47fff1">
|
:http-request="uploadImg" style="--el-color-primary:#47fff1">
|
||||||
<el-image v-if="params.img !== ''" :src="params.img" fit="cover"/>
|
<el-image v-if="params.img !== ''" :src="params.img" fit="cover"/>
|
||||||
<el-icon v-else class="uploader-icon">
|
<el-icon v-else class="uploader-icon">
|
||||||
<Plus/>
|
<Plus/>
|
||||||
@ -219,21 +219,8 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="图生文(可选)" name="图生文">
|
<div class="prompt-box">
|
||||||
<div class="text">图生文功能正在紧锣密鼓开发中,敬请期待...</div>
|
|
||||||
<!-- <div class="param-line pt">-->
|
|
||||||
<!-- <el-empty image-size="100px" description="功能建设中"/>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
</el-tab-pane>
|
|
||||||
|
|
||||||
<el-tab-pane label="融图(可选)" name="融图">
|
|
||||||
<div class="text">融图功能正在紧锣密鼓开发中,敬请期待...</div>
|
|
||||||
</el-tab-pane>
|
|
||||||
</el-tabs>
|
|
||||||
|
|
||||||
<div v-loading="loading" element-loading-background="rgba(122, 122, 122, 0.8)">
|
|
||||||
<div class="param-line pt">
|
<div class="param-line pt">
|
||||||
<div class="flex-row justify-between items-center">
|
<div class="flex-row justify-between items-center">
|
||||||
<div class="flex-row justify-start items-center">
|
<div class="flex-row justify-start items-center">
|
||||||
@ -245,7 +232,7 @@
|
|||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<el-button type="primary" @click="translatePrompt">
|
<el-button type="primary" @click="translatePrompt(false)" :disabled="loading">
|
||||||
<el-icon style="margin-right: 6px;font-size: 18px;">
|
<el-icon style="margin-right: 6px;font-size: 18px;">
|
||||||
<Refresh/>
|
<Refresh/>
|
||||||
</el-icon>
|
</el-icon>
|
||||||
@ -259,7 +246,7 @@
|
|||||||
content="使用 AI 翻译并重写提示词,<br/>增加更多细节,风格等描述"
|
content="使用 AI 翻译并重写提示词,<br/>增加更多细节,风格等描述"
|
||||||
placement="top-end"
|
placement="top-end"
|
||||||
>
|
>
|
||||||
<el-button type="success" @click="rewritePrompt">
|
<el-button type="success" @click="rewritePrompt" :disabled="loading">
|
||||||
<el-icon style="margin-right: 6px;font-size: 18px;">
|
<el-icon style="margin-right: 6px;font-size: 18px;">
|
||||||
<Refresh/>
|
<Refresh/>
|
||||||
</el-icon>
|
</el-icon>
|
||||||
@ -275,7 +262,6 @@
|
|||||||
ref="promptRef"
|
ref="promptRef"
|
||||||
placeholder="这里输入你的英文咒语,例如:A chinese girl walking in the middle of a cobblestone street"/>
|
placeholder="这里输入你的英文咒语,例如:A chinese girl walking in the middle of a cobblestone street"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="param-line pt">
|
<div class="param-line pt">
|
||||||
<div class="flex-row justify-between items-center">
|
<div class="flex-row justify-between items-center">
|
||||||
@ -287,12 +273,12 @@
|
|||||||
</el-icon>
|
</el-icon>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<!-- <el-button type="success">-->
|
<el-button type="primary" @click="translatePrompt(true)" :disabled="loading">
|
||||||
<!-- <el-icon style="margin-right: 6px;font-size: 18px;">-->
|
<el-icon style="margin-right: 6px;font-size: 18px;">
|
||||||
<!-- <Refresh/>-->
|
<Refresh/>
|
||||||
<!-- </el-icon>-->
|
</el-icon>
|
||||||
<!-- 翻译-->
|
翻译
|
||||||
<!-- </el-button>-->
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -301,6 +287,51 @@
|
|||||||
ref="promptRef"
|
ref="promptRef"
|
||||||
placeholder="这里输入你不希望出现在图片上的内容,元素"/>
|
placeholder="这里输入你不希望出现在图片上的内容,元素"/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="融图(可选)" name="blend">
|
||||||
|
<div class="text">请上传两张以上的图片</div>
|
||||||
|
<div class="img-inline">
|
||||||
|
<el-upload class="img-uploader" :auto-upload="true" :show-file-list="false"
|
||||||
|
:http-request="uploadImg" style="--el-color-primary:#47fff1">
|
||||||
|
<el-image v-if="params.img !== ''" :src="params.img" fit="cover"/>
|
||||||
|
<el-icon v-else class="uploader-icon">
|
||||||
|
<Plus/>
|
||||||
|
</el-icon>
|
||||||
|
</el-upload>
|
||||||
|
|
||||||
|
<el-upload class="img-uploader" :auto-upload="true" :show-file-list="false"
|
||||||
|
:http-request="uploadImg2" style="--el-color-primary:#47fff1">
|
||||||
|
<el-image v-if="params.img2 !== ''" :src="params.img2" fit="cover"/>
|
||||||
|
<el-icon v-else class="uploader-icon">
|
||||||
|
<Plus/>
|
||||||
|
</el-icon>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="换脸(可选)" name="swapFace">
|
||||||
|
<div class="text">请上传两张有脸部的图片,用右边图片的脸替换左边图片的脸</div>
|
||||||
|
<div class="img-inline">
|
||||||
|
<el-upload class="img-uploader" :auto-upload="true" :show-file-list="false"
|
||||||
|
:http-request="uploadImg" style="--el-color-primary:#47fff1">
|
||||||
|
<el-image v-if="params.img !== ''" :src="params.img" fit="cover"/>
|
||||||
|
<el-icon v-else class="uploader-icon">
|
||||||
|
<Plus/>
|
||||||
|
</el-icon>
|
||||||
|
</el-upload>
|
||||||
|
|
||||||
|
<el-upload class="img-uploader" :auto-upload="true" :show-file-list="false"
|
||||||
|
:http-request="uploadImg2" style="--el-color-primary:#47fff1">
|
||||||
|
<el-image v-if="params.img2 !== ''" :src="params.img2" fit="cover"/>
|
||||||
|
<el-icon v-else class="uploader-icon">
|
||||||
|
<Plus/>
|
||||||
|
</el-icon>
|
||||||
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
<div class="submit-btn">
|
<div class="submit-btn">
|
||||||
<el-button color="#47fff1" :dark="false" @click="generate" round>立即生成</el-button>
|
<el-button color="#47fff1" :dark="false" @click="generate" round>立即生成</el-button>
|
||||||
@ -516,6 +547,7 @@ const options = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const params = ref({
|
const params = ref({
|
||||||
|
task_type: "image",
|
||||||
rate: rates[0].value,
|
rate: rates[0].value,
|
||||||
model: models[0].value,
|
model: models[0].value,
|
||||||
chaos: 0,
|
chaos: 0,
|
||||||
@ -523,6 +555,8 @@ const params = ref({
|
|||||||
seed: 0,
|
seed: 0,
|
||||||
raw: false,
|
raw: false,
|
||||||
img: "",
|
img: "",
|
||||||
|
img2: "",
|
||||||
|
img_arr: [],
|
||||||
weight: 0.25,
|
weight: 0.25,
|
||||||
prompt: "",
|
prompt: "",
|
||||||
neg_prompt: "",
|
neg_prompt: "",
|
||||||
@ -530,7 +564,7 @@ const params = ref({
|
|||||||
quality: 0
|
quality: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
const activeName = ref('图生图')
|
const activeName = ref('image')
|
||||||
|
|
||||||
const runningJobs = ref([])
|
const runningJobs = ref([])
|
||||||
const finishedJobs = ref([])
|
const finishedJobs = ref([])
|
||||||
@ -552,10 +586,18 @@ const rewritePrompt = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const translatePrompt = () => {
|
const translatePrompt = (negative) => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
httpPost("/api/prompt/translate", {"prompt": params.value.prompt}).then(res => {
|
let prompt = params.value.prompt
|
||||||
|
if (negative) {
|
||||||
|
prompt = params.value.neg_prompt
|
||||||
|
}
|
||||||
|
httpPost("/api/prompt/translate", {"prompt": prompt}).then(res => {
|
||||||
|
if (negative) {
|
||||||
|
params.value.neg_prompt = res.data
|
||||||
|
} else {
|
||||||
params.value.prompt = res.data
|
params.value.prompt = res.data
|
||||||
|
}
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
@ -684,7 +726,7 @@ const changeModel = (item) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 图片上传
|
// 图片上传
|
||||||
const afterRead = (file) => {
|
const uploadImg = (file) => {
|
||||||
// 压缩图片并上传
|
// 压缩图片并上传
|
||||||
new Compressor(file.file, {
|
new Compressor(file.file, {
|
||||||
quality: 0.6,
|
quality: 0.6,
|
||||||
@ -704,10 +746,30 @@ const afterRead = (file) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const uploadImg2 = (file) => {
|
||||||
|
// 压缩图片并上传
|
||||||
|
new Compressor(file.file, {
|
||||||
|
quality: 0.6,
|
||||||
|
success(result) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', result, result.name);
|
||||||
|
// 执行上传操作
|
||||||
|
httpPost('/api/upload', formData).then((res) => {
|
||||||
|
params.value.img2 = res.data.url
|
||||||
|
ElMessage.success('上传成功')
|
||||||
|
}).catch((e) => {
|
||||||
|
ElMessage.error('上传失败:' + e.message)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
error(err) {
|
||||||
|
console.log(err.message);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
// 创建绘图任务
|
// 创建绘图任务
|
||||||
const promptRef = ref(null)
|
const promptRef = ref(null)
|
||||||
const generate = () => {
|
const generate = () => {
|
||||||
if (params.value.prompt === '') {
|
if (params.value.prompt === '' && params.value.task_type === "image") {
|
||||||
promptRef.value.focus()
|
promptRef.value.focus()
|
||||||
return ElMessage.error("请输入绘画提示词!")
|
return ElMessage.error("请输入绘画提示词!")
|
||||||
}
|
}
|
||||||
@ -715,6 +777,12 @@ const generate = () => {
|
|||||||
return ElMessage.error("动漫模型不允许启用原始模式")
|
return ElMessage.error("动漫模型不允许启用原始模式")
|
||||||
}
|
}
|
||||||
params.value.session_id = getSessionId()
|
params.value.session_id = getSessionId()
|
||||||
|
if (params.value.img !== "") {
|
||||||
|
params.value.img_arr.push(params.value.img)
|
||||||
|
}
|
||||||
|
if (params.value.img2 !== "") {
|
||||||
|
params.value.img_arr.push(params.value.img2)
|
||||||
|
}
|
||||||
httpPost("/api/mj/image", params.value).then(() => {
|
httpPost("/api/mj/image", params.value).then(() => {
|
||||||
ElMessage.success("绘画任务推送成功,请耐心等待任务执行...")
|
ElMessage.success("绘画任务推送成功,请耐心等待任务执行...")
|
||||||
imgCalls.value -= 1
|
imgCalls.value -= 1
|
||||||
@ -725,7 +793,6 @@ const generate = () => {
|
|||||||
|
|
||||||
// 图片放大任务
|
// 图片放大任务
|
||||||
const upscale = (index, item) => {
|
const upscale = (index, item) => {
|
||||||
console.log(item)
|
|
||||||
send('/api/mj/upscale', index, item)
|
send('/api/mj/upscale', index, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -783,6 +850,11 @@ const publishImage = (item, action) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 切换菜单
|
||||||
|
const tabChange = (tab) => {
|
||||||
|
params.value.task_type = tab
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus">
|
<style lang="stylus">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mobile-user-profile container">
|
<div class="mobile-mj container">
|
||||||
<van-nav-bar :title="title"/>
|
<van-nav-bar :title="title"/>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@ -56,7 +56,7 @@ import {showFailToast, showNotify, showSuccessToast} from "vant";
|
|||||||
import {httpGet, httpPost} from "@/utils/http";
|
import {httpGet, httpPost} from "@/utils/http";
|
||||||
import Compressor from 'compressorjs';
|
import Compressor from 'compressorjs';
|
||||||
|
|
||||||
const title = ref('用户设置')
|
const title = ref('MidJourney 绘画')
|
||||||
const form = ref({
|
const form = ref({
|
||||||
username: '',
|
username: '',
|
||||||
nickname: '',
|
nickname: '',
|
||||||
@ -116,13 +116,6 @@ const save = () => {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus">
|
<style lang="stylus" scoped>
|
||||||
.mobile-user-profile {
|
@import "@/assets/css/mobile/image-mj.styl"
|
||||||
.content {
|
|
||||||
.van-field__label {
|
|
||||||
width 100px
|
|
||||||
text-align right
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
Loading…
Reference in New Issue
Block a user