1、添加中英文版本
2、修复已知BUG 3、完善功能 4、添加minimax视频渠道
This commit is contained in:
88
api/handlers/audio_extraction.go
Normal file
88
api/handlers/audio_extraction.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/drama-generator/backend/application/services"
|
||||
"github.com/drama-generator/backend/pkg/logger"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type AudioExtractionHandler struct {
|
||||
service *services.AudioExtractionService
|
||||
log *logger.Logger
|
||||
dataDir string
|
||||
}
|
||||
|
||||
func NewAudioExtractionHandler(log *logger.Logger, dataDir string) *AudioExtractionHandler {
|
||||
return &AudioExtractionHandler{
|
||||
service: services.NewAudioExtractionService(log),
|
||||
log: log,
|
||||
dataDir: dataDir,
|
||||
}
|
||||
}
|
||||
|
||||
// ExtractAudio 提取单个视频的音频
|
||||
// @Summary 提取视频音频
|
||||
// @Description 从视频URL中提取音频轨道
|
||||
// @Tags Audio
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body services.ExtractAudioRequest true "提取请求"
|
||||
// @Success 200 {object} services.ExtractAudioResponse
|
||||
// @Router /api/audio/extract [post]
|
||||
func (h *AudioExtractionHandler) ExtractAudio(c *gin.Context) {
|
||||
var req services.ExtractAudioRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.log.Errorw("Invalid request body", "error", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
h.log.Infow("Received audio extraction request", "video_url", req.VideoURL)
|
||||
|
||||
result, err := h.service.ExtractAudio(req.VideoURL, h.dataDir)
|
||||
if err != nil {
|
||||
h.log.Errorw("Failed to extract audio", "error", err, "video_url", req.VideoURL)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
type BatchExtractAudioRequest struct {
|
||||
VideoURLs []string `json:"video_urls" binding:"required,min=1"`
|
||||
}
|
||||
|
||||
// BatchExtractAudio 批量提取音频
|
||||
// @Summary 批量提取视频音频
|
||||
// @Description 从多个视频URL中提取音频轨道
|
||||
// @Tags Audio
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body BatchExtractAudioRequest true "批量提取请求"
|
||||
// @Success 200 {array} services.ExtractAudioResponse
|
||||
// @Router /api/audio/extract/batch [post]
|
||||
func (h *AudioExtractionHandler) BatchExtractAudio(c *gin.Context) {
|
||||
var req BatchExtractAudioRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.log.Errorw("Invalid request body", "error", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"})
|
||||
return
|
||||
}
|
||||
|
||||
h.log.Infow("Received batch audio extraction request", "count", len(req.VideoURLs))
|
||||
|
||||
results, err := h.service.BatchExtractAudio(req.VideoURLs, h.dataDir)
|
||||
if err != nil {
|
||||
h.log.Errorw("Failed to batch extract audio", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"results": results,
|
||||
"total": len(results),
|
||||
})
|
||||
}
|
||||
@@ -21,7 +21,7 @@ type CharacterLibraryHandler struct {
|
||||
func NewCharacterLibraryHandler(db *gorm.DB, cfg *config.Config, log *logger.Logger, transferService *services2.ResourceTransferService, localStorage *storage.LocalStorage) *CharacterLibraryHandler {
|
||||
return &CharacterLibraryHandler{
|
||||
libraryService: services2.NewCharacterLibraryService(db, log),
|
||||
imageService: services2.NewImageGenerationService(db, transferService, localStorage, log),
|
||||
imageService: services2.NewImageGenerationService(db, cfg, transferService, localStorage, log),
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,24 +27,22 @@ func (h *FramePromptHandler) GenerateFramePrompt(c *gin.Context) {
|
||||
storyboardID := c.Param("id")
|
||||
|
||||
var req struct {
|
||||
FrameType string `json:"frame_type" binding:"required"` // first, key, last, panel, action
|
||||
FrameType string `json:"frame_type"`
|
||||
PanelCount int `json:"panel_count"`
|
||||
Model string `json:"model"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "Invalid request body")
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 构建请求
|
||||
serviceReq := services.GenerateFramePromptRequest{
|
||||
StoryboardID: storyboardID,
|
||||
FrameType: services.FrameType(req.FrameType),
|
||||
PanelCount: req.PanelCount,
|
||||
}
|
||||
|
||||
// 生成提示词
|
||||
result, err := h.framePromptService.GenerateFramePrompt(serviceReq)
|
||||
result, err := h.framePromptService.GenerateFramePrompt(serviceReq, req.Model)
|
||||
if err != nil {
|
||||
h.log.Errorw("Failed to generate frame prompt", "error", err)
|
||||
response.InternalError(c, err.Error())
|
||||
|
||||
@@ -20,7 +20,7 @@ type ImageGenerationHandler struct {
|
||||
|
||||
func NewImageGenerationHandler(db *gorm.DB, cfg *config.Config, log *logger.Logger, transferService *services.ResourceTransferService, localStorage *storage.LocalStorage) *ImageGenerationHandler {
|
||||
return &ImageGenerationHandler{
|
||||
imageService: services.NewImageGenerationService(db, transferService, localStorage, log),
|
||||
imageService: services.NewImageGenerationService(db, cfg, transferService, localStorage, log),
|
||||
taskService: services.NewTaskService(db, log),
|
||||
log: log,
|
||||
}
|
||||
@@ -75,6 +75,15 @@ func (h *ImageGenerationHandler) GetBackgroundsForEpisode(c *gin.Context) {
|
||||
func (h *ImageGenerationHandler) ExtractBackgroundsForEpisode(c *gin.Context) {
|
||||
episodeID := c.Param("episode_id")
|
||||
|
||||
// 接收可选的 model 参数
|
||||
var req struct {
|
||||
Model string `json:"model"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
// 如果没有提供body或者解析失败,使用空字符串(使用默认模型)
|
||||
req.Model = ""
|
||||
}
|
||||
|
||||
// 创建异步任务
|
||||
task, err := h.taskService.CreateTask("background_extraction", episodeID)
|
||||
if err != nil {
|
||||
@@ -84,7 +93,7 @@ func (h *ImageGenerationHandler) ExtractBackgroundsForEpisode(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 启动后台goroutine处理
|
||||
go h.processBackgroundExtraction(task.ID, episodeID)
|
||||
go h.processBackgroundExtraction(task.ID, episodeID, req.Model)
|
||||
|
||||
// 立即返回任务ID
|
||||
response.Success(c, gin.H{
|
||||
@@ -95,8 +104,8 @@ func (h *ImageGenerationHandler) ExtractBackgroundsForEpisode(c *gin.Context) {
|
||||
}
|
||||
|
||||
// processBackgroundExtraction 后台处理场景提取
|
||||
func (h *ImageGenerationHandler) processBackgroundExtraction(taskID, episodeID string) {
|
||||
h.log.Infow("Starting background extraction", "task_id", taskID, "episode_id", episodeID)
|
||||
func (h *ImageGenerationHandler) processBackgroundExtraction(taskID, episodeID, model string) {
|
||||
h.log.Infow("Starting background extraction", "task_id", taskID, "episode_id", episodeID, "model", model)
|
||||
|
||||
// 更新任务状态为处理中
|
||||
if err := h.taskService.UpdateTaskStatus(taskID, "processing", 10, "开始提取场景..."); err != nil {
|
||||
@@ -104,7 +113,7 @@ func (h *ImageGenerationHandler) processBackgroundExtraction(taskID, episodeID s
|
||||
}
|
||||
|
||||
// 调用实际的提取逻辑
|
||||
backgrounds, err := h.imageService.ExtractBackgroundsForEpisode(episodeID)
|
||||
backgrounds, err := h.imageService.ExtractBackgroundsForEpisode(episodeID, model)
|
||||
if err != nil {
|
||||
h.log.Errorw("Failed to extract backgrounds", "error", err, "task_id", taskID)
|
||||
if updateErr := h.taskService.UpdateTaskError(taskID, err); updateErr != nil {
|
||||
|
||||
@@ -73,3 +73,19 @@ func (h *SceneHandler) GenerateSceneImage(c *gin.Context) {
|
||||
"image_generation": imageGen,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *SceneHandler) DeleteScene(c *gin.Context) {
|
||||
sceneID := c.Param("scene_id")
|
||||
|
||||
if err := h.sceneService.DeleteScene(sceneID); err != nil {
|
||||
h.log.Errorw("Failed to delete scene", "error", err, "scene_id", sceneID)
|
||||
if err.Error() == "scene not found" {
|
||||
response.NotFound(c, "场景不存在")
|
||||
return
|
||||
}
|
||||
response.InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{"message": "场景已删除"})
|
||||
}
|
||||
|
||||
@@ -17,30 +17,12 @@ type ScriptGenerationHandler struct {
|
||||
|
||||
func NewScriptGenerationHandler(db *gorm.DB, cfg *config.Config, log *logger.Logger) *ScriptGenerationHandler {
|
||||
return &ScriptGenerationHandler{
|
||||
scriptService: services.NewScriptGenerationService(db, log),
|
||||
scriptService: services.NewScriptGenerationService(db, cfg, log),
|
||||
taskService: services.NewTaskService(db, log),
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ScriptGenerationHandler) GenerateOutline(c *gin.Context) {
|
||||
|
||||
var req services.GenerateOutlineRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.scriptService.GenerateOutline(&req)
|
||||
if err != nil {
|
||||
h.log.Errorw("Failed to generate outline", "error", err)
|
||||
response.InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, result)
|
||||
}
|
||||
|
||||
func (h *ScriptGenerationHandler) GenerateCharacters(c *gin.Context) {
|
||||
var req services.GenerateCharactersRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@@ -56,8 +38,11 @@ func (h *ScriptGenerationHandler) GenerateCharacters(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 复制req值,避免goroutine中使用指针导致的并发问题
|
||||
reqCopy := req
|
||||
|
||||
// 启动后台goroutine处理
|
||||
go h.processCharacterGeneration(task.ID, &req)
|
||||
go h.processCharacterGeneration(task.ID, &reqCopy)
|
||||
|
||||
// 立即返回任务ID
|
||||
response.Success(c, gin.H{
|
||||
@@ -98,21 +83,3 @@ func (h *ScriptGenerationHandler) processCharacterGeneration(taskID string, req
|
||||
|
||||
h.log.Infow("Character generation completed", "task_id", taskID, "total", len(characters))
|
||||
}
|
||||
|
||||
func (h *ScriptGenerationHandler) GenerateEpisodes(c *gin.Context) {
|
||||
|
||||
var req services.GenerateEpisodesRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
episodes, err := h.scriptService.GenerateEpisodes(&req)
|
||||
if err != nil {
|
||||
h.log.Errorw("Failed to generate episodes", "error", err)
|
||||
response.InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, episodes)
|
||||
}
|
||||
|
||||
67
api/handlers/settings.go
Normal file
67
api/handlers/settings.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/drama-generator/backend/pkg/config"
|
||||
"github.com/drama-generator/backend/pkg/logger"
|
||||
"github.com/drama-generator/backend/pkg/response"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type SettingsHandler struct {
|
||||
config *config.Config
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func NewSettingsHandler(cfg *config.Config, log *logger.Logger) *SettingsHandler {
|
||||
return &SettingsHandler{
|
||||
config: cfg,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
// GetLanguage 获取当前系统语言
|
||||
func (h *SettingsHandler) GetLanguage(c *gin.Context) {
|
||||
language := h.config.App.Language
|
||||
if language == "" {
|
||||
language = "zh" // 默认中文
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"language": language,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateLanguage 更新系统语言
|
||||
func (h *SettingsHandler) UpdateLanguage(c *gin.Context) {
|
||||
var req struct {
|
||||
Language string `json:"language" binding:"required,oneof=zh en"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "语言参数错误,只支持 zh 或 en")
|
||||
return
|
||||
}
|
||||
|
||||
// 更新内存中的配置
|
||||
h.config.App.Language = req.Language
|
||||
|
||||
// 更新配置文件
|
||||
viper.Set("app.language", req.Language)
|
||||
if err := viper.WriteConfig(); err != nil {
|
||||
h.log.Warnw("Failed to write config file", "error", err)
|
||||
// 即使写入文件失败,内存配置也已更新,仍然可用
|
||||
}
|
||||
|
||||
h.log.Infow("System language updated", "language", req.Language)
|
||||
|
||||
message := "语言已切换为中文"
|
||||
if req.Language == "en" {
|
||||
message = "Language switched to English"
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": message,
|
||||
"language": req.Language,
|
||||
})
|
||||
}
|
||||
@@ -17,7 +17,7 @@ type StoryboardHandler struct {
|
||||
|
||||
func NewStoryboardHandler(db *gorm.DB, cfg *config.Config, log *logger.Logger) *StoryboardHandler {
|
||||
return &StoryboardHandler{
|
||||
storyboardService: services.NewStoryboardService(db, log),
|
||||
storyboardService: services.NewStoryboardService(db, cfg, log),
|
||||
taskService: services.NewTaskService(db, log),
|
||||
log: log,
|
||||
}
|
||||
@@ -27,6 +27,15 @@ func NewStoryboardHandler(db *gorm.DB, cfg *config.Config, log *logger.Logger) *
|
||||
func (h *StoryboardHandler) GenerateStoryboard(c *gin.Context) {
|
||||
episodeID := c.Param("episode_id")
|
||||
|
||||
// 接收可选的 model 参数
|
||||
var req struct {
|
||||
Model string `json:"model"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
// 如果没有提供body或者解析失败,使用空字符串(使用默认模型)
|
||||
req.Model = ""
|
||||
}
|
||||
|
||||
// 创建异步任务
|
||||
task, err := h.taskService.CreateTask("storyboard_generation", episodeID)
|
||||
if err != nil {
|
||||
@@ -36,19 +45,19 @@ func (h *StoryboardHandler) GenerateStoryboard(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 启动后台goroutine处理
|
||||
go h.processStoryboardGeneration(task.ID, episodeID)
|
||||
go h.processStoryboardGeneration(task.ID, episodeID, req.Model)
|
||||
|
||||
// 立即返回任务ID
|
||||
response.Success(c, gin.H{
|
||||
"task_id": task.ID,
|
||||
"status": "pending",
|
||||
"message": "分镜生成任务已创建,正在后台处理...",
|
||||
"message": "分镜头生成任务已创建,正在后台处理...",
|
||||
})
|
||||
}
|
||||
|
||||
// processStoryboardGeneration 后台处理分镜生成
|
||||
func (h *StoryboardHandler) processStoryboardGeneration(taskID, episodeID string) {
|
||||
h.log.Infow("Starting storyboard generation", "task_id", taskID, "episode_id", episodeID)
|
||||
func (h *StoryboardHandler) processStoryboardGeneration(taskID, episodeID, model string) {
|
||||
h.log.Infow("Starting storyboard generation", "task_id", taskID, "episode_id", episodeID, "model", model)
|
||||
|
||||
// 更新任务状态为处理中
|
||||
if err := h.taskService.UpdateTaskStatus(taskID, "processing", 10, "开始生成分镜..."); err != nil {
|
||||
@@ -56,7 +65,7 @@ func (h *StoryboardHandler) processStoryboardGeneration(taskID, episodeID string
|
||||
}
|
||||
|
||||
// 调用实际的生成逻辑
|
||||
result, err := h.storyboardService.GenerateStoryboard(episodeID)
|
||||
result, err := h.storyboardService.GenerateStoryboard(episodeID, model)
|
||||
if err != nil {
|
||||
h.log.Errorw("Failed to generate storyboard", "error", err, "task_id", taskID)
|
||||
if updateErr := h.taskService.UpdateTaskError(taskID, err); updateErr != nil {
|
||||
|
||||
Reference in New Issue
Block a user