save task origin info for AI generating jobs

This commit is contained in:
RockYang 2024-11-11 17:22:08 +08:00
parent 135755d21d
commit ce8a2d0222
31 changed files with 358 additions and 136 deletions

View File

@ -74,7 +74,7 @@ type SdTaskParams struct {
// DallTask DALL-E task // DallTask DALL-E task
type DallTask struct { type DallTask struct {
ClientId string `json:"client_id"` ClientId string `json:"client_id"`
JobId uint `json:"job_id"` Id uint `json:"id"`
UserId uint `json:"user_id"` UserId uint `json:"user_id"`
Prompt string `json:"prompt"` Prompt string `json:"prompt"`
N int `json:"n"` N int `json:"n"`

View File

@ -72,10 +72,21 @@ func (h *DallJobHandler) Image(c *gin.Context) {
idValue, _ := c.Get(types.LoginUserID) idValue, _ := c.Get(types.LoginUserID)
userId := utils.IntValue(utils.InterfaceToString(idValue), 0) userId := utils.IntValue(utils.InterfaceToString(idValue), 0)
task := types.DallTask{
ClientId: data.ClientId,
UserId: uint(userId),
Prompt: data.Prompt,
Quality: data.Quality,
Size: data.Size,
Style: data.Style,
Power: h.App.SysConfig.DallPower,
TranslateModelId: h.App.SysConfig.TranslateModelId,
}
job := model.DallJob{ job := model.DallJob{
UserId: uint(userId), UserId: uint(userId),
Prompt: data.Prompt, Prompt: data.Prompt,
Power: h.App.SysConfig.DallPower, Power: task.Power,
TaskInfo: utils.JsonEncode(task),
} }
res := h.DB.Create(&job) res := h.DB.Create(&job)
if res.Error != nil { if res.Error != nil {
@ -83,17 +94,8 @@ func (h *DallJobHandler) Image(c *gin.Context) {
return return
} }
h.dallService.PushTask(types.DallTask{ task.Id = job.Id
ClientId: data.ClientId, h.dallService.PushTask(task)
JobId: job.Id,
UserId: uint(userId),
Prompt: data.Prompt,
Quality: data.Quality,
Size: data.Size,
Style: data.Style,
Power: job.Power,
TranslateModelId: h.App.SysConfig.TranslateModelId,
})
resp.SUCCESS(c) resp.SUCCESS(c)
} }

View File

@ -212,7 +212,7 @@ func (h *FunctionHandler) Dall3(c *gin.Context) {
} }
content, err := h.dallService.Image(types.DallTask{ content, err := h.dallService.Image(types.DallTask{
JobId: job.Id, Id: job.Id,
UserId: user.Id, UserId: user.Id,
Prompt: job.Prompt, Prompt: job.Prompt,
N: 1, N: 1,

View File

@ -152,10 +152,23 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
resp.ERROR(c, "error with generate task id: "+err.Error()) resp.ERROR(c, "error with generate task id: "+err.Error())
return return
} }
task := types.MjTask{
ClientId: data.ClientId,
TaskId: taskId,
Type: types.TaskType(data.TaskType),
Prompt: data.Prompt,
NegPrompt: data.NegPrompt,
Params: params,
UserId: userId,
ImgArr: data.ImgArr,
Mode: h.App.SysConfig.MjMode,
TranslateModelId: h.App.SysConfig.TranslateModelId,
}
job := model.MidJourneyJob{ job := model.MidJourneyJob{
Type: data.TaskType, Type: data.TaskType,
UserId: userId, UserId: userId,
TaskId: taskId, TaskId: taskId,
TaskInfo: utils.JsonEncode(task),
Progress: 0, Progress: 0,
Prompt: fmt.Sprintf("%s %s", data.Prompt, params), Prompt: fmt.Sprintf("%s %s", data.Prompt, params),
Power: h.App.SysConfig.MjPower, Power: h.App.SysConfig.MjPower,
@ -175,19 +188,8 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
return return
} }
h.mjService.PushTask(types.MjTask{ task.Id = job.Id
Id: job.Id, h.mjService.PushTask(task)
ClientId: data.ClientId,
TaskId: taskId,
Type: types.TaskType(data.TaskType),
Prompt: data.Prompt,
NegPrompt: data.NegPrompt,
Params: params,
UserId: userId,
ImgArr: data.ImgArr,
Mode: h.App.SysConfig.MjMode,
TranslateModelId: h.App.SysConfig.TranslateModelId,
})
// update user's power // update user's power
err = h.userService.DecreasePower(job.UserId, job.Power, model.PowerLog{ err = h.userService.DecreasePower(job.UserId, job.Power, model.PowerLog{
@ -226,10 +228,21 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) {
idValue, _ := c.Get(types.LoginUserID) idValue, _ := c.Get(types.LoginUserID)
userId := utils.IntValue(utils.InterfaceToString(idValue), 0) userId := utils.IntValue(utils.InterfaceToString(idValue), 0)
taskId, _ := h.snowflake.Next(true) taskId, _ := h.snowflake.Next(true)
task := types.MjTask{
ClientId: data.ClientId,
Type: types.TaskUpscale,
UserId: userId,
ChannelId: data.ChannelId,
Index: data.Index,
MessageId: data.MessageId,
MessageHash: data.MessageHash,
Mode: h.App.SysConfig.MjMode,
}
job := model.MidJourneyJob{ job := model.MidJourneyJob{
Type: types.TaskUpscale.String(), Type: types.TaskUpscale.String(),
UserId: userId, UserId: userId,
TaskId: taskId, TaskId: taskId,
TaskInfo: utils.JsonEncode(task),
Progress: 0, Progress: 0,
Power: h.App.SysConfig.MjActionPower, Power: h.App.SysConfig.MjActionPower,
CreatedAt: time.Now(), CreatedAt: time.Now(),
@ -239,17 +252,8 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) {
return return
} }
h.mjService.PushTask(types.MjTask{ task.Id = job.Id
Id: job.Id, h.mjService.PushTask(task)
ClientId: data.ClientId,
Type: types.TaskUpscale,
UserId: userId,
ChannelId: data.ChannelId,
Index: data.Index,
MessageId: data.MessageId,
MessageHash: data.MessageHash,
Mode: h.App.SysConfig.MjMode,
})
// update user's power // update user's power
err := h.userService.DecreasePower(job.UserId, job.Power, model.PowerLog{ err := h.userService.DecreasePower(job.UserId, job.Power, model.PowerLog{
@ -280,11 +284,22 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) {
idValue, _ := c.Get(types.LoginUserID) idValue, _ := c.Get(types.LoginUserID)
userId := utils.IntValue(utils.InterfaceToString(idValue), 0) userId := utils.IntValue(utils.InterfaceToString(idValue), 0)
taskId, _ := h.snowflake.Next(true) taskId, _ := h.snowflake.Next(true)
task := types.MjTask{
Type: types.TaskVariation,
ClientId: data.ClientId,
UserId: userId,
Index: data.Index,
ChannelId: data.ChannelId,
MessageId: data.MessageId,
MessageHash: data.MessageHash,
Mode: h.App.SysConfig.MjMode,
}
job := model.MidJourneyJob{ job := model.MidJourneyJob{
Type: types.TaskVariation.String(), Type: types.TaskVariation.String(),
ChannelId: data.ChannelId, ChannelId: data.ChannelId,
UserId: userId, UserId: userId,
TaskId: taskId, TaskId: taskId,
TaskInfo: utils.JsonEncode(task),
Progress: 0, Progress: 0,
Power: h.App.SysConfig.MjActionPower, Power: h.App.SysConfig.MjActionPower,
CreatedAt: time.Now(), CreatedAt: time.Now(),
@ -294,17 +309,8 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) {
return return
} }
h.mjService.PushTask(types.MjTask{ task.Id = job.Id
Id: job.Id, h.mjService.PushTask(task)
Type: types.TaskVariation,
ClientId: data.ClientId,
UserId: userId,
Index: data.Index,
ChannelId: data.ChannelId,
MessageId: data.MessageId,
MessageHash: data.MessageHash,
Mode: h.App.SysConfig.MjMode,
})
err := h.userService.DecreasePower(job.UserId, job.Power, model.PowerLog{ err := h.userService.DecreasePower(job.UserId, job.Power, model.PowerLog{
Type: types.PowerConsume, Type: types.PowerConsume,

View File

@ -18,6 +18,7 @@ import (
"geekai/utils/resp" "geekai/utils/resp"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gorm.io/gorm" "gorm.io/gorm"
"strings"
) )
// 提示词生成 handler // 提示词生成 handler
@ -57,3 +58,39 @@ func (h *PromptHandler) Lyric(c *gin.Context) {
resp.SUCCESS(c, content) resp.SUCCESS(c, content)
} }
// Image 生成 AI 绘画提示词
func (h *PromptHandler) Image(c *gin.Context) {
var data struct {
Prompt string `json:"prompt"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
content, err := utils.OpenAIRequest(h.DB, fmt.Sprintf(service.ImagePromptOptimizeTemplate, data.Prompt), h.App.SysConfig.TranslateModelId)
if err != nil {
resp.ERROR(c, err.Error())
return
}
resp.SUCCESS(c, strings.Trim(content, `"`))
}
// Video 生成视频提示词
func (h *PromptHandler) Video(c *gin.Context) {
var data struct {
Prompt string `json:"prompt"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
return
}
content, err := utils.OpenAIRequest(h.DB, fmt.Sprintf(service.VideoPromptTemplate, data.Prompt), h.App.SysConfig.TranslateModelId)
if err != nil {
resp.ERROR(c, err.Error())
return
}
resp.SUCCESS(c, strings.Trim(content, `"`))
}

View File

@ -89,13 +89,29 @@ func (h *SunoHandler) Create(c *gin.Context) {
data.Prompt = fmt.Sprintf("%s\n%s", song.Prompt, refSong.Prompt) data.Prompt = fmt.Sprintf("%s\n%s", song.Prompt, refSong.Prompt)
} }
} }
task := types.SunoTask{
ClientId: data.ClientId,
UserId: int(h.GetLoginUserId(c)),
Type: data.Type,
Title: data.Title,
RefTaskId: data.RefTaskId,
RefSongId: data.RefSongId,
ExtendSecs: data.ExtendSecs,
Prompt: data.Prompt,
Tags: data.Tags,
Model: data.Model,
Instrumental: data.Instrumental,
SongId: data.SongId,
AudioURL: data.AudioURL,
}
// 插入数据库 // 插入数据库
job := model.SunoJob{ job := model.SunoJob{
UserId: int(h.GetLoginUserId(c)), UserId: task.UserId,
Prompt: data.Prompt, Prompt: data.Prompt,
Instrumental: data.Instrumental, Instrumental: data.Instrumental,
ModelName: data.Model, ModelName: data.Model,
TaskInfo: utils.JsonEncode(task),
Tags: data.Tags, Tags: data.Tags,
Title: data.Title, Title: data.Title,
Type: data.Type, Type: data.Type,
@ -115,26 +131,13 @@ func (h *SunoHandler) Create(c *gin.Context) {
} }
// 创建任务 // 创建任务
h.sunoService.PushTask(types.SunoTask{ task.Id = job.Id
ClientId: data.ClientId, h.sunoService.PushTask(task)
Id: job.Id,
UserId: job.UserId,
Type: job.Type,
Title: job.Title,
RefTaskId: data.RefTaskId,
RefSongId: data.RefSongId,
ExtendSecs: data.ExtendSecs,
Prompt: job.Prompt,
Tags: data.Tags,
Model: data.Model,
Instrumental: data.Instrumental,
SongId: data.SongId,
AudioURL: data.AudioURL,
})
// update user's power // update user's power
err = h.userService.DecreasePower(job.UserId, job.Power, model.PowerLog{ err = h.userService.DecreasePower(job.UserId, job.Power, model.PowerLog{
Type: types.PowerConsume, Type: types.PowerConsume,
Model: job.ModelName,
Remark: fmt.Sprintf("Suno 文生歌曲,%s", job.ModelName), Remark: fmt.Sprintf("Suno 文生歌曲,%s", job.ModelName),
CreatedAt: time.Now(), CreatedAt: time.Now(),
}) })

View File

@ -80,13 +80,21 @@ func (h *VideoHandler) LumaCreate(c *gin.Context) {
StartImgURL: data.FirstFrameImg, StartImgURL: data.FirstFrameImg,
EndImgURL: data.EndFrameImg, EndImgURL: data.EndFrameImg,
} }
task := types.VideoTask{
ClientId: data.ClientId,
UserId: userId,
Type: types.VideoLuma,
Prompt: data.Prompt,
Params: params,
TranslateModelId: h.App.SysConfig.TranslateModelId,
}
// 插入数据库 // 插入数据库
job := model.VideoJob{ job := model.VideoJob{
UserId: userId, UserId: userId,
Type: types.VideoLuma, Type: types.VideoLuma,
Prompt: data.Prompt, Prompt: data.Prompt,
Power: h.App.SysConfig.LumaPower, Power: h.App.SysConfig.LumaPower,
Params: utils.JsonEncode(params), TaskInfo: utils.JsonEncode(task),
} }
tx := h.DB.Create(&job) tx := h.DB.Create(&job)
if tx.Error != nil { if tx.Error != nil {
@ -95,15 +103,8 @@ func (h *VideoHandler) LumaCreate(c *gin.Context) {
} }
// 创建任务 // 创建任务
h.videoService.PushTask(types.VideoTask{ task.Id = job.Id
ClientId: data.ClientId, h.videoService.PushTask(task)
Id: job.Id,
UserId: userId,
Type: types.VideoLuma,
Prompt: data.Prompt,
Params: params,
TranslateModelId: h.App.SysConfig.TranslateModelId,
})
// update user's power // update user's power
err = h.userService.DecreasePower(job.UserId, job.Power, model.PowerLog{ err = h.userService.DecreasePower(job.UserId, job.Power, model.PowerLog{

View File

@ -521,6 +521,8 @@ func main() {
fx.Invoke(func(s *core.AppServer, h *handler.PromptHandler) { fx.Invoke(func(s *core.AppServer, h *handler.PromptHandler) {
group := s.Engine.Group("/api/prompt") group := s.Engine.Group("/api/prompt")
group.POST("/lyric", h.Lyric) group.POST("/lyric", h.Lyric)
group.POST("/image", h.Image)
group.POST("/video", h.Video)
}), }),
fx.Invoke(func(s *core.AppServer, db *gorm.DB) { fx.Invoke(func(s *core.AppServer, db *gorm.DB) {
go func() { go func() {

View File

@ -59,6 +59,20 @@ func (s *Service) PushTask(task types.DallTask) {
} }
func (s *Service) Run() { func (s *Service) Run() {
// 将数据库中未提交的人物加载到队列
var jobs []model.DallJob
s.db.Where("progress", 0).Find(&jobs)
for _, v := range jobs {
var task types.DallTask
err := utils.JsonDecode(v.TaskInfo, &task)
if err != nil {
logger.Errorf("decode task info with error: %v", err)
continue
}
task.Id = v.Id
s.PushTask(task)
}
logger.Info("Starting DALL-E job consumer...") logger.Info("Starting DALL-E job consumer...")
go func() { go func() {
for { for {
@ -69,15 +83,15 @@ func (s *Service) Run() {
continue continue
} }
logger.Infof("handle a new DALL-E task: %+v", task) logger.Infof("handle a new DALL-E task: %+v", task)
s.clientIds[task.JobId] = task.ClientId s.clientIds[task.Id] = task.ClientId
_, err = s.Image(task, false) _, err = s.Image(task, false)
if err != nil { if err != nil {
logger.Errorf("error with image task: %v", err) logger.Errorf("error with image task: %v", err)
s.db.Model(&model.DallJob{Id: task.JobId}).UpdateColumns(map[string]interface{}{ s.db.Model(&model.DallJob{Id: task.Id}).UpdateColumns(map[string]interface{}{
"progress": service.FailTaskProgress, "progress": service.FailTaskProgress,
"err_msg": err.Error(), "err_msg": err.Error(),
}) })
s.notifyQueue.RPush(service.NotifyMessage{ClientId: task.ClientId, UserId: int(task.UserId), JobId: int(task.JobId), Message: service.TaskStatusFailed}) s.notifyQueue.RPush(service.NotifyMessage{ClientId: task.ClientId, UserId: int(task.UserId), JobId: int(task.Id), Message: service.TaskStatusFailed})
} }
} }
}() }()
@ -177,7 +191,7 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
// update the api key last use time // update the api key last use time
s.db.Model(&apiKey).UpdateColumn("last_used_at", time.Now().Unix()) s.db.Model(&apiKey).UpdateColumn("last_used_at", time.Now().Unix())
// update task progress // update task progress
err = s.db.Model(&model.DallJob{Id: task.JobId}).UpdateColumns(map[string]interface{}{ err = s.db.Model(&model.DallJob{Id: task.Id}).UpdateColumns(map[string]interface{}{
"progress": 100, "progress": 100,
"org_url": res.Data[0].Url, "org_url": res.Data[0].Url,
"prompt": prompt, "prompt": prompt,
@ -186,10 +200,10 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
return "", fmt.Errorf("err with update database: %v", err) return "", fmt.Errorf("err with update database: %v", err)
} }
s.notifyQueue.RPush(service.NotifyMessage{ClientId: task.ClientId, UserId: int(task.UserId), JobId: int(task.JobId), Message: service.TaskStatusFailed}) s.notifyQueue.RPush(service.NotifyMessage{ClientId: task.ClientId, UserId: int(task.UserId), JobId: int(task.Id), Message: service.TaskStatusFailed})
var content string var content string
if sync { if sync {
imgURL, err := s.downloadImage(task.JobId, int(task.UserId), res.Data[0].Url) imgURL, err := s.downloadImage(task.Id, int(task.UserId), res.Data[0].Url)
if err != nil { if err != nil {
return "", fmt.Errorf("error with download image: %v", err) return "", fmt.Errorf("error with download image: %v", err)
} }

View File

@ -46,6 +46,21 @@ func NewService(redisCli *redis.Client, db *gorm.DB, client *Client, manager *os
} }
func (s *Service) Run() { func (s *Service) Run() {
// 将数据库中未提交的人物加载到队列
var jobs []model.MidJourneyJob
s.db.Where("task_id", "").Where("progress", 0).Find(&jobs)
for _, v := range jobs {
var task types.MjTask
err := utils.JsonDecode(v.TaskInfo, &task)
if err != nil {
logger.Errorf("decode task info with error: %v", err)
continue
}
task.Id = v.Id
s.clientIds[task.Id] = task.ClientId
s.PushTask(task)
}
logger.Info("Starting MidJourney job consumer for service") logger.Info("Starting MidJourney job consumer for service")
go func() { go func() {
for { for {

View File

@ -58,22 +58,17 @@ func (s *Service) PushTask(task types.SunoTask) {
func (s *Service) Run() { func (s *Service) Run() {
// 将数据库中未提交的人物加载到队列 // 将数据库中未提交的人物加载到队列
var jobs []model.SunoJob var jobs []model.SunoJob
s.db.Where("task_id", "").Find(&jobs) s.db.Where("task_id", "").Where("progress", 0).Find(&jobs)
for _, v := range jobs { for _, v := range jobs {
s.PushTask(types.SunoTask{ var task types.SunoTask
Id: v.Id, err := utils.JsonDecode(v.TaskInfo, &task)
Channel: v.Channel, if err != nil {
UserId: v.UserId, logger.Errorf("decode task info with error: %v", err)
Type: v.Type, continue
Title: v.Title, }
RefTaskId: v.RefTaskId, task.Id = v.Id
RefSongId: v.RefSongId, s.PushTask(task)
Prompt: v.Prompt, s.clientIds[v.TaskId] = task.ClientId
Tags: v.Tags,
Model: v.ModelName,
Instrumental: v.Instrumental,
ExtendSecs: v.ExtendSecs,
})
} }
logger.Info("Starting Suno job consumer...") logger.Info("Starting Suno job consumer...")
go func() { go func() {

View File

@ -90,3 +90,29 @@ const LyricPromptTemplate = `
尾声 尾声
{{歌词内容}} {{歌词内容}}
` `
const VideoPromptTemplate = `
As an expert in video generation prompts, please create a detailed descriptive prompt for the following video concept. The description should include the setting, character appearance, actions, overall atmosphere, and camera angles. Please make it as detailed and vivid as possible to help ensure that every aspect of the video is accurately captured.
Please remember that regardless of the users input, the final output must be in English.
# Details to Include
- Describe the overall visual style of the video (e.g., animated, realistic, retro tone, etc.)
- Identify key characters or objects in the video and describe their appearance, attire, and expressions
- Describe the environment of the scene, including weather, lighting, colors, and important details
- Explain the behavior and interactions of the characters
- Include any unique camera angles, movements, or special effects
# Output Format
Provide the prompt in paragraph form, ensuring that the description is detailed enough for a video generation system to recreate the envisioned scene. Include the beginning, middle, and end of the scene to convey a complete storyline.
# Example
**User Input:**
A small cat basking in the sun on a balcony.
**Generated Prompt:**
On a bright spring afternoon, an orange-striped kitten lies lazily on a balcony, basking in the warm sunlight. The iron railings around the balcony cast soft shadows that dance gently with the light. The cats eyes are half-closed, exuding a sense of contentment and tranquility in its surroundings. In the distance, a few fluffy white clouds drift slowly across the blue sky. The camera initially focuses on the cats face, capturing the delicate details of its fur, and then gradually zooms out to reveal the full balcony scene, immersing viewers in a moment of calm and relaxation.
The theme of the creation is:%s
`

View File

@ -60,20 +60,15 @@ func (s *Service) Run() {
var jobs []model.VideoJob var jobs []model.VideoJob
s.db.Where("task_id", "").Where("progress", 0).Find(&jobs) s.db.Where("task_id", "").Where("progress", 0).Find(&jobs)
for _, v := range jobs { for _, v := range jobs {
var params types.VideoParams var task types.VideoTask
if err := utils.JsonDecode(v.Params, &params); err != nil { err := utils.JsonDecode(v.TaskInfo, &task)
logger.Errorf("unmarshal params failed: %v", err) if err != nil {
logger.Errorf("decode task info with error: %v", err)
continue continue
} }
s.PushTask(types.VideoTask{ task.Id = v.Id
Id: v.Id, s.PushTask(task)
Channel: v.Channel, s.clientIds[v.Id] = task.ClientId
UserId: v.UserId,
Type: v.Type,
TaskId: v.TaskId,
Prompt: v.Prompt,
Params: params,
})
} }
logger.Info("Starting Video job consumer...") logger.Info("Starting Video job consumer...")
go func() { go func() {

View File

@ -6,6 +6,7 @@ type DallJob struct {
Id uint `gorm:"primarykey;column:id"` Id uint `gorm:"primarykey;column:id"`
UserId uint UserId uint
Prompt string Prompt string
TaskInfo string // 原始任务信息
ImgURL string ImgURL string
OrgURL string OrgURL string
Publish bool Publish bool

View File

@ -7,6 +7,7 @@ type MidJourneyJob struct {
Type string Type string
UserId int UserId int
TaskId string TaskId string
TaskInfo string // 原始任务信息
ChannelId string ChannelId string
MessageId string MessageId string
ReferenceId string ReferenceId string

View File

@ -9,6 +9,7 @@ type SunoJob struct {
Title string Title string
Type int Type int
TaskId string TaskId string
TaskInfo string // 原始任务信息
RefTaskId string // 续写的任务id RefTaskId string // 续写的任务id
Tags string // 歌曲风格和标签 Tags string // 歌曲风格和标签
Instrumental bool // 是否生成纯音乐 Instrumental bool // 是否生成纯音乐

View File

@ -8,6 +8,7 @@ type VideoJob struct {
Channel string // 频道 Channel string // 频道
Type string // luma,runway,cog Type string // luma,runway,cog
TaskId string TaskId string
TaskInfo string // 原始任务信息
Prompt string // 提示词 Prompt string // 提示词
PromptExt string // 优化后提示词 PromptExt string // 优化后提示词
CoverURL string // 封面图 URL CoverURL string // 封面图 URL
@ -18,7 +19,6 @@ type VideoJob struct {
ErrMsg string // 错误信息 ErrMsg string // 错误信息
RawData string // 原始数据 json RawData string // 原始数据 json
Power int // 消耗算力 Power int // 消耗算力
Params string // 任务参数
CreatedAt time.Time CreatedAt time.Time
} }

View File

@ -1,7 +1,5 @@
package vo package vo
import "geekai/core/types"
type VideoJob struct { type VideoJob struct {
Id uint `json:"id"` Id uint `json:"id"`
UserId int `json:"user_id"` UserId int `json:"user_id"`
@ -18,6 +16,5 @@ type VideoJob struct {
ErrMsg string `json:"err_msg"` // 错误信息 ErrMsg string `json:"err_msg"` // 错误信息
RawData map[string]interface{} `json:"raw_data"` // 原始数据 json RawData map[string]interface{} `json:"raw_data"` // 原始数据 json
Power int `json:"power"` // 消耗算力 Power int `json:"power"` // 消耗算力
Params types.VideoParams `json:"params"` // 任务参数
CreatedAt int64 `json:"created_at"` CreatedAt int64 `json:"created_at"`
} }

View File

@ -1 +1,5 @@
ALTER TABLE `chatgpt_sd_jobs` ADD `task_info` TEXT NOT NULL COMMENT '任务详情' AFTER `task_id`; ALTER TABLE `chatgpt_sd_jobs` ADD `task_info` TEXT NOT NULL COMMENT '任务详情' AFTER `task_id`;
ALTER TABLE `chatgpt_mj_jobs` ADD `task_info` TEXT NOT NULL COMMENT '任务详情' AFTER `task_id`;
ALTER TABLE `chatgpt_dall_jobs` ADD `task_info` TEXT NOT NULL COMMENT '任务详情' AFTER `prompt`;
ALTER TABLE `chatgpt_suno_jobs` ADD `task_info` TEXT NOT NULL COMMENT '任务详情' AFTER `task_id`;
ALTER TABLE `chatgpt_video_jobs` CHANGE `params` `task_info` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '原始任务信息' AFTER `task_id`;

View File

@ -473,8 +473,13 @@
padding 30px padding 30px
} }
} }
}
.generate-btn {
.iconfont {
margin-right 5px
}
}
}
} }
.mj-list-item-prompt { .mj-list-item-prompt {

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4125778 */ font-family: "iconfont"; /* Project id 4125778 */
src: url('iconfont.woff2?t=1728891448746') format('woff2'), src: url('iconfont.woff2?t=1731289567907') format('woff2'),
url('iconfont.woff?t=1728891448746') format('woff'), url('iconfont.woff?t=1731289567907') format('woff'),
url('iconfont.ttf?t=1728891448746') format('truetype'); url('iconfont.ttf?t=1731289567907') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,14 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-linggan:before {
content: "\e641";
}
.icon-chuangzuo:before {
content: "\e6cc";
}
.icon-call:before { .icon-call:before {
content: "\e769"; content: "\e769";
} }

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,20 @@
"css_prefix_text": "icon-", "css_prefix_text": "icon-",
"description": "", "description": "",
"glyphs": [ "glyphs": [
{
"icon_id": "15330210",
"name": "创意灵感",
"font_class": "linggan",
"unicode": "e641",
"unicode_decimal": 58945
},
{
"icon_id": "39170417",
"name": "创作",
"font_class": "chuangzuo",
"unicode": "e6cc",
"unicode_decimal": 59084
},
{ {
"icon_id": "11231556", "icon_id": "11231556",
"name": "打电话", "name": "打电话",

Binary file not shown.

View File

@ -59,10 +59,17 @@
:autosize="{ minRows: 4, maxRows: 6 }" :autosize="{ minRows: 4, maxRows: 6 }"
type="textarea" type="textarea"
ref="promptRef" ref="promptRef"
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词" placeholder="请在此输入绘画提示词,您也可以点击下面的提示词助手生成绘画提示词"
/> />
</div> </div>
<el-row class="text-info">
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2" :disabled="isGenerating">
<i class="iconfont icon-chuangzuo" style="margin-right: 5px"></i>
<span>生成专业绘画指令</span>
</el-button>
</el-row>
<div class="text-info"> <div class="text-info">
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="12"> <el-col :span="12">
@ -212,6 +219,7 @@ import {checkSession, getClientId, getSystemInfo} from "@/store/cache";
import {useSharedStore} from "@/store/sharedata"; import {useSharedStore} from "@/store/sharedata";
import TaskList from "@/components/TaskList.vue"; import TaskList from "@/components/TaskList.vue";
import BackTop from "@/components/BackTop.vue"; import BackTop from "@/components/BackTop.vue";
import {showMessageError} from "@/utils/dialog";
const listBoxHeight = ref(0) const listBoxHeight = ref(0)
// const paramBoxHeight = ref(0) // const paramBoxHeight = ref(0)
@ -410,6 +418,21 @@ const publishImage = (item, action) => {
}) })
} }
const isGenerating = ref(false)
const generatePrompt = () => {
if (params.value.prompt === "") {
return showMessageError("请输入原始提示词")
}
isGenerating.value = true
httpPost("/api/prompt/image", {prompt: params.value.prompt}).then(res => {
params.value.prompt = res.data
isGenerating.value = false
}).catch(e => {
showMessageError("生成提示词失败:"+e.message)
isGenerating.value = false
})
}
</script> </script>
<style lang="stylus"> <style lang="stylus">

View File

@ -21,7 +21,6 @@
<div class="flex-col items-center" <div class="flex-col items-center"
:class="item.value === params.rate ? 'grid-content active' : 'grid-content'" :class="item.value === params.rate ? 'grid-content active' : 'grid-content'"
@click="changeRate(item)"> @click="changeRate(item)">
<!-- <div :class="'shape ' + item.css"></div>-->
<el-image class="icon" :src="item.img" fit="cover"></el-image> <el-image class="icon" :src="item.img" fit="cover"></el-image>
<div class="text">{{ item.text }}</div> <div class="text">{{ item.text }}</div>
</div> </div>
@ -183,12 +182,19 @@
</div> </div>
</div> </div>
<div class="param-line pt"> <div class="param-line pt" style="position: relative">
<el-input v-model="params.prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea" <el-input v-model="params.prompt" :autosize="{ minRows: 4, maxRows: 6 }" type="textarea"
ref="promptRef" ref="promptRef"
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/> placeholder="请在此输入绘画提示词,您也可以点击下面的提示词助手生成绘画提示词"/>
</div> </div>
<el-row class="text-info">
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2" :disabled="isGenerating">
<i class="iconfont icon-chuangzuo"></i>
<span>生成专业绘画指令</span>
</el-button>
</el-row>
<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">
@ -268,6 +274,13 @@
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/> placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词"/>
</div> </div>
<el-row class="text-info">
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2" :disabled="isGenerating">
<i class="iconfont icon-chuangzuo"></i>
<span>生成专业绘画指令</span>
</el-button>
</el-row>
<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">
@ -615,6 +628,7 @@ import {copyObj, removeArrayItem} from "@/utils/libs";
import {useSharedStore} from "@/store/sharedata"; import {useSharedStore} from "@/store/sharedata";
import TaskList from "@/components/TaskList.vue"; import TaskList from "@/components/TaskList.vue";
import BackTop from "@/components/BackTop.vue"; import BackTop from "@/components/BackTop.vue";
import {showMessageError} from "@/utils/dialog";
const listBoxHeight = ref(0) const listBoxHeight = ref(0)
const paramBoxHeight = ref(0) const paramBoxHeight = ref(0)
@ -644,10 +658,10 @@ const rates = [
{css: "size9-16", value: "9:16", text: "9:16", img: "/images/mj/rate_9_16.png"}, {css: "size9-16", value: "9:16", text: "9:16", img: "/images/mj/rate_9_16.png"},
] ]
const models = [ const models = [
{text: "写实模式MJ-6.0", value: " --v 6", img: "/images/mj/mj-v6.png"}, {text: "写实模式MJ-6.1", value: " --v 6.1", img: "/images/mj/mj-v6.png"},
{text: "优质模式MJ-5.2", value: " --v 5.2", img: "/images/mj/mj-v5.2.png"}, {text: "优质模式MJ-6.0", value: " --v 6", img: "/images/mj/mj-v5.2.png"},
{text: "优质模式MJ-5.1", value: " --v 5.1", img: "/images/mj/mj-v5.1.jpg"}, {text: "优质模式MJ-5.2", value: " --v 5.2", img: "/images/mj/mj-v5.1.jpg"},
{text: "虚幻模式MJ-5", value: " --v 5", img: "/images/mj/mj-v5.jpg"}, {text: "虚幻模式MJ-5.1", value: " --v 5.1", img: "/images/mj/mj-v5.jpg"},
{text: "真实模式MJ-4", value: " --v 4", img: "/images/mj/mj-v4.jpg"}, {text: "真实模式MJ-4", value: " --v 4", img: "/images/mj/mj-v4.jpg"},
{text: "动漫风-niji4", value: " --niji 4", img: "/images/mj/nj4.jpg"}, {text: "动漫风-niji4", value: " --niji 4", img: "/images/mj/nj4.jpg"},
{text: "动漫风-niji5", value: " --niji 5", img: "/images/mj/mj-niji.png"}, {text: "动漫风-niji5", value: " --niji 5", img: "/images/mj/mj-niji.png"},
@ -820,7 +834,7 @@ const fetchFinishJobs = () => {
jobs[i]['thumb_url'] = '/images/img-placeholder.jpg' jobs[i]['thumb_url'] = '/images/img-placeholder.jpg'
} }
if ((jobs[i].type === 'image' || jobs[i].type === 'variation') && jobs[i].progress === 100) { if (jobs[i].type !== 'upscale' && jobs[i].progress === 100){
jobs[i]['can_opt'] = true jobs[i]['can_opt'] = true
} }
} }
@ -999,6 +1013,21 @@ const removeUploadImage = (url) => {
imgList.value = removeArrayItem(imgList.value, url) imgList.value = removeArrayItem(imgList.value, url)
} }
const isGenerating = ref(false)
const generatePrompt = () => {
if (params.value.prompt === "") {
return showMessageError("请输入原始提示词")
}
isGenerating.value = true
httpPost("/api/prompt/image", {prompt: params.value.prompt}).then(res => {
params.value.prompt = res.data
isGenerating.value = false
}).catch(e => {
showMessageError("生成提示词失败:"+e.message)
isGenerating.value = false
})
}
</script> </script>
<style lang="stylus"> <style lang="stylus">

View File

@ -250,10 +250,17 @@
:autosize="{ minRows: 4, maxRows: 6 }" :autosize="{ minRows: 4, maxRows: 6 }"
type="textarea" type="textarea"
ref="promptRef" ref="promptRef"
placeholder="请在此输入绘画提示词,系统会自动翻译中文提示词,高手请直接输入英文提示词" placeholder="请在此输入绘画提示词,您也可以点击下面的提示词助手生成绘画提示词"
/> />
</div> </div>
<el-row class="text-info">
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2" :disabled="isGenerating">
<i class="iconfont icon-chuangzuo" style="margin-right: 5px"></i>
<span>生成专业绘画指令</span>
</el-button>
</el-row>
<div class="param-line pt"> <div class="param-line pt">
<span>反向提示词</span> <span>反向提示词</span>
<el-tooltip <el-tooltip
@ -497,6 +504,7 @@ import {getSessionId} from "@/store/session";
import {useSharedStore} from "@/store/sharedata"; import {useSharedStore} from "@/store/sharedata";
import TaskList from "@/components/TaskList.vue"; import TaskList from "@/components/TaskList.vue";
import BackTop from "@/components/BackTop.vue"; import BackTop from "@/components/BackTop.vue";
import {showMessageError} from "@/utils/dialog";
const listBoxHeight = ref(0) const listBoxHeight = ref(0)
// const paramBoxHeight = ref(0) // const paramBoxHeight = ref(0)
@ -722,6 +730,21 @@ const publishImage = (item, action) => {
}) })
} }
const isGenerating = ref(false)
const generatePrompt = () => {
if (params.value.prompt === "") {
return showMessageError("请输入原始提示词")
}
isGenerating.value = true
httpPost("/api/prompt/image", {prompt: params.value.prompt}).then(res => {
params.value.prompt = res.data
isGenerating.value = false
}).catch(e => {
showMessageError("生成提示词失败:"+e.message)
isGenerating.value = false
})
}
</script> </script>
<style lang="stylus"> <style lang="stylus">

View File

@ -38,6 +38,12 @@
</div> </div>
<div class="params"> <div class="params">
<div class="item-group">
<el-button class="generate-btn" size="small" @click="generatePrompt" color="#5865f2" :disabled="isGenerating">
<i class="iconfont icon-chuangzuo" style="margin-right: 5px"></i>
<span>生成AI视频提示词</span>
</el-button>
</div>
<div class="item-group"> <div class="item-group">
<span class="label">循环参考图</span> <span class="label">循环参考图</span>
<el-switch v-model="formData.loop" size="small" style="--el-switch-on-color:#BF78BF;" /> <el-switch v-model="formData.loop" size="small" style="--el-switch-on-color:#BF78BF;" />
@ -294,6 +300,20 @@ const create = () => {
}) })
} }
const isGenerating = ref(false)
const generatePrompt = () => {
if (formData.prompt === "") {
return showMessageError("请输入原始提示词")
}
isGenerating.value = true
httpPost("/api/prompt/image", {prompt: formData.prompt}).then(res => {
formData.prompt = res.data
isGenerating.value = false
}).catch(e => {
showMessageError("生成提示词失败:"+e.message)
isGenerating.value = false
})
}
</script> </script>

View File

@ -434,7 +434,7 @@ const fetchFinishJobs = (page) => {
jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/480/q/75' jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/480/q/75'
} }
if ((jobs[i].type === 'image' || jobs[i].type === 'variation') && jobs[i].progress === 100){ if (jobs[i].type !== 'upscale' && jobs[i].progress === 100){
jobs[i]['can_opt'] = true jobs[i]['can_opt'] = true
} }
} }