欢迎光临
我们一直在努力

当AI学会拍短剧:Huobao Drama全栈AI短剧生成平台深度解析

从一个想法到一部完整短剧,AI只需要几分钟。这不是科幻,这是正在发生的技术革命。

前言:一个程序员的"导演梦"

还记得小时候看电视剧,总幻想自己能当导演,拍出惊天动地的大片。长大后才发现,拍一部短剧需要编剧、导演、摄影、演员、后期……光是凑齐这些人就够喝一壶的了,更别提那动辄几十万的制作成本。

但是,如果我告诉你,现在只需要一个创意、一台电脑、几个API Key,就能让AI帮你完成从剧本创作到视频成片的全流程,你信吗?

今天要介绍的这个开源项目——Huobao Drama,就是这样一个"让程序员也能当导演"的神器。它基于Go + Vue3全栈开发,采用DDD领域驱动设计,集成了OpenAI、Gemini、火山引擎等多家AI服务,实现了短剧制作的全流程自动化。

废话不多说,让我们一起深入这个项目的技术内核,看看它是如何把"AI拍短剧"这个看似天方夜谭的想法变成现实的。


一、项目全景:从创意到成片的AI流水线

1.1 核心工作流程

Huobao Drama的核心理念可以用一句话概括:让AI成为你的剧组。

整个系统的工作流程如下:

创意输入 → 剧本生成 → 角色设计 → 场景提取 → 分镜拆解 → 图片生成 → 视频生成 → 视频合成 → 成片输出

每一个环节都由AI驱动,人类只需要在关键节点进行审核和调整。这种"人机协作"的模式,既保证了创作效率,又保留了人类的创意主导权。

1.2 技术栈一览

后端技术栈:

  • 语言:Go 1.23+

  • Web框架:Gin 1.9+

  • ORM:GORM

  • 数据库:SQLite(支持WAL模式,解决并发问题)

  • 日志:Zap

  • 视频处理:FFmpeg

  • AI服务:OpenAI、Gemini、火山引擎等

前端技术栈:

  • 框架:Vue 3.4+

  • 语言:TypeScript 5+

  • 构建工具:Vite 5

  • UI组件:Element Plus

  • CSS框架:TailwindCSS

  • 状态管理:Pinia

  • 路由:Vue Router 4

看到这个技术栈,是不是感觉很"正经"?没错,这不是一个玩具项目,而是一个可以真正投入生产使用的全栈应用。


二、架构设计:DDD领域驱动的优雅实践

2.1 分层架构

Huobao Drama采用了经典的DDD分层架构,代码组织清晰明了:

├── api/                    # API层 – HTTP接口
│   ├── handlers/          # 请求处理器
│   ├── middlewares/       # 中间件
│   └── routes/            # 路由配置
├── application/           # 应用服务层 – 业务逻辑编排
│   └── services/          # 各类业务服务
├── domain/                # 领域层 – 核心业务模型
│   └── models/            # 领域模型定义
├── infrastructure/        # 基础设施层 – 外部依赖
│   ├── database/          # 数据库连接
│   ├── external/          # 外部服务(FFmpeg等)
│   ├── scheduler/         # 定时任务
│   └── storage/           # 存储服务
└── pkg/                   # 工具包
    ├── ai/                # AI客户端
    ├── image/             # 图片生成客户端
    ├── video/             # 视频生成客户端
    └── …

这种分层设计的好处是什么?关注点分离。每一层只负责自己的事情:

  • API层负责接收请求、参数校验、返回响应

  • 应用服务层负责业务流程编排

  • 领域层负责核心业务规则

  • 基础设施层负责与外部系统交互

2.2 领域模型设计

让我们看看核心的领域模型是如何设计的:

// Drama – 剧本/项目
type Drama struct {
    ID            uint           `gorm:"primaryKey;autoIncrement" json:"id"`
    Title         string         `gorm:"type:varchar(200);not null" json:"title"`
    Description   *string        `gorm:"type:text" json:"description"`
    Genre         *string        `gorm:"type:varchar(50)" json:"genre"`
    Status        string         `gorm:"type:varchar(20);default:'draft'" json:"status"`
    
    Episodes   []Episode   `gorm:"foreignKey:DramaID" json:"episodes,omitempty"`
    Characters []Character `gorm:"foreignKey:DramaID" json:"characters,omitempty"`
    Scenes     []Scene     `gorm:"foreignKey:DramaID" json:"scenes,omitempty"`
}

// Episode – 剧集/章节
type Episode struct {
    ID            uint      `gorm:"primaryKey;autoIncrement" json:"id"`
    DramaID       uint      `gorm:"not null;index" json:"drama_id"`
    EpisodeNum    int       `gorm:"column:episode_number;not null" json:"episode_number"`
    Title         string    `gorm:"type:varchar(200);not null" json:"title"`
    ScriptContent *string   `gorm:"type:longtext" json:"script_content"`
    Duration      int       `gorm:"default:0" json:"duration"`
    
    Storyboards []Storyboard `gorm:"foreignKey:EpisodeID" json:"storyboards,omitempty"`
    Characters  []Character  `gorm:"many2many:episode_characters;" json:"characters,omitempty"`
}

// Storyboard – 分镜
type Storyboard struct {
    ID               uint    `gorm:"primaryKey;autoIncrement" json:"id"`
    EpisodeID        uint    `gorm:"not null;index" json:"episode_id"`
    StoryboardNumber int     `gorm:"not null" json:"storyboard_number"`
    Title            *string `gorm:"size:255" json:"title"`
    Location         *string `gorm:"size:255" json:"location"`
    Time             *string `gorm:"size:255" json:"time"`
    ShotType         *string `gorm:"size:100" json:"shot_type"`      // 景别
    Movement         *string `gorm:"size:100" json:"movement"`       // 运镜
    Action           *string `gorm:"type:text" json:"action"`        // 动作
    Dialogue         *string `gorm:"type:text" json:"dialogue"`      // 对话
    ImagePrompt      *string `gorm:"type:text" json:"image_prompt"`  // 图片提示词
    VideoPrompt      *string `gorm:"type:text" json:"video_prompt"`  // 视频提示词
    Duration         int     `gorm:"default:5" json:"duration"`      // 时长(秒)
    ComposedImage    *string `gorm:"type:text" json:"composed_image"`
    VideoURL         *string `gorm:"type:text" json:"video_url"`
    
    Characters []Character `gorm:"many2many:storyboard_characters;" json:"characters,omitempty"`
}

这个模型设计体现了几个关键思想:

  • 层级关系清晰:Drama → Episode → Storyboard,形成了"剧本-剧集-分镜"的三级结构

  • 多对多关系:角色可以出现在多个剧集和分镜中,通过关联表实现

  • 双提示词设计:每个分镜同时生成ImagePrompt和VideoPrompt,分别用于图片和视频生成


  • 三、AI服务集成:多模型协同的艺术

    3.1 统一的AI客户端接口

    Huobao Drama最精妙的设计之一,就是它的AI服务抽象层。系统支持多种AI服务提供商,但对上层业务来说,调用方式完全一致。

    // AIClient 定义文本生成客户端接口
    type AIClient interface {
        GenerateText(prompt string, systemPrompt string, options …func(*ChatCompletionRequest)) (string, error)
        TestConnection() error
    }

    这个简洁的接口背后,是对OpenAI、Gemini等多种AI服务的统一封装。无论你用的是GPT-4还是Gemini Pro,调用方式都是一样的:

    text, err := client.GenerateText(userPrompt, systemPrompt, ai.WithTemperature(0.7))

    3.2 图片生成客户端

    图片生成同样采用了接口抽象的设计:

    type ImageClient interface {
        GenerateImage(prompt string, opts …ImageOption) (*ImageResult, error)
        GetTaskStatus(taskID string) (*ImageResult, error)
    }

    系统支持多种图片生成服务:

    • OpenAI DALL-E

    • Google Gemini

    • 火山引擎(豆包)

    每种服务都有自己的实现类,但对外暴露的接口完全一致。这种设计的好处是:切换AI服务提供商只需要修改配置,不需要改动业务代码。

    3.3 视频生成客户端

    视频生成是整个系统最复杂的部分,因为不同的视频生成服务差异很大:

    type VideoClient interface {
        GenerateVideo(imageURL, prompt string, opts …VideoOption) (*VideoResult, error)
        GetTaskStatus(taskID string) (*VideoResult, error)
    }

    系统支持的视频生成服务包括:

    • 火山引擎(豆包视频)

    • Runway

    • Pika

    • OpenAI Sora

    • Minimax

    每种服务的API格式、参数名称、返回结构都不一样,但通过适配器模式,系统将它们统一成了相同的接口。

    3.4 异步任务处理

    AI生成任务通常需要较长时间,系统采用了异步处理+轮询的模式:

    func (s *VideoGenerationService) ProcessVideoGeneration(videoGenID uint) {
        // 1. 更新状态为处理中
        s.db.Model(&videoGen).Update("status", models.VideoStatusProcessing)
        
        // 2. 调用AI服务
        result, err := client.GenerateVideo(imageURL, videoGen.Prompt, opts…)
        
        // 3. 如果返回任务ID,启动轮询
        if result.TaskID != "" {
            go s.pollTaskStatus(videoGenID, result.TaskID, videoGen.Provider, videoGen.Model)
            return
        }
        
        // 4. 如果直接返回结果,完成任务
        if result.VideoURL != "" {
            s.completeVideoGeneration(videoGenID, result.VideoURL, …)
        }
    }

    轮询逻辑也很优雅:

    func (s *VideoGenerationService) pollTaskStatus(videoGenID uint, taskID string, …) {
        maxAttempts := 300  // 最多轮询300次
        interval := 10 * time.Second  // 每10秒轮询一次
        
        for attempt := 0; attempt < maxAttempts; attempt++ {
            time.Sleep(interval)
            
            result, err := client.GetTaskStatus(taskID)
            if result.Completed {
                s.completeVideoGeneration(videoGenID, result.VideoURL, …)
                return
            }
            if result.Error != "" {
                s.updateVideoGenError(videoGenID, result.Error)
                return
            }
        }
        
        s.updateVideoGenError(videoGenID, "polling timeout")
    }

    这种设计保证了:

  • 用户提交任务后立即返回,不会阻塞

  • 后台持续跟踪任务状态

  • 任务完成后自动更新数据库

  • 超时或失败都有明确的错误处理


  • 四、分镜生成:AI导演的核心能力

    4.1 从剧本到分镜的魔法

    分镜生成是整个系统最核心的AI能力。它需要AI理解剧本内容,然后像一个专业导演一样,把文字描述转化为一个个具体的镜头。

    看看这个精心设计的提示词模板:

    func (s *StoryboardService) GenerateStoryboard(episodeID string, model string) (*GenerateStoryboardResult, error) {
        // 获取剧本内容
        scriptContent := *episode.ScriptContent
        
        // 获取角色列表(包含ID,供AI引用)
        characterList := fmt.Sprintf("[%s]", strings.Join(charInfoList, ", "))
        
        // 获取场景列表(包含ID,供AI引用)
        sceneList := fmt.Sprintf("[%s]", strings.Join(sceneInfoList, ", "))
        
        // 构建提示词
        prompt := fmt.Sprintf(`
    【分镜要素】每个镜头聚焦单一动作,描述要详尽具体:
    1. **镜头标题(title)**:用3-5个字概括该镜头的核心内容或情绪
    2. **时间**:[清晨/午后/深夜/具体时分+详细光线描述]
    3. **地点**:[场景完整描述+空间布局+环境细节]
    4. **镜头设计**:
       – **景别(shot_type)**:[远景/全景/中景/近景/特写]
       – **镜头角度(angle)**:[平视/仰视/俯视/侧面/背面]
       – **运镜方式(movement)**:[固定镜头/推镜/拉镜/摇镜/跟镜/移镜]
    5. **人物行为**:详细动作描述,包含[谁+具体怎么做+肢体细节+表情状态]
    6. **对话/独白**:提取该镜头中的完整对话或独白内容
    7. **画面结果**:动作的即时后果+视觉细节+氛围变化
    8. **环境氛围**:光线质感+色调+声音环境+整体氛围
    9. **配乐提示(bgm_prompt)**:描述该镜头配乐的氛围、节奏、情绪
    10. **音效描述(sound_effect)**:描述该镜头的关键音效
    11. **观众情绪**:[情绪类型]([强度:↑↑↑/↑↑/↑/→/↓] + [落点:悬置/释放/反转])

        `, scriptContent, characterList, sceneList)
    }

    4.2 时长估算算法

    分镜的时长估算是一个很有意思的问题。系统设计了一套基于规则的估算算法:

    **duration时长估算规则(秒)**:
    – **所有镜头时长必须在4-12秒范围内**

    **估算步骤**:
    1. **基础时长**:
       – 纯对话场景:基础4秒
       – 纯动作场景:基础5秒
       – 对话+动作混合:基础6秒

    2. **对话调整**:
       – 无对话:+0秒
       – 短对话(1-20字):+1-2秒
       – 中等对话(21-50字):+2-4秒
       – 长对话(51字以上):+4-6秒

    3. **动作调整**:
       – 简单动作:+0-1秒
       – 一般动作:+1-2秒
       – 复杂动作:+2-4秒
       – 环境展示:+2-5秒

    4. **最终时长** = 基础时长 + 对话调整 + 动作调整

    这套规则让AI能够为每个分镜估算出合理的时长,最终所有分镜时长之和就是整集的总时长。

    4.3 双提示词生成

    每个分镜会同时生成两种提示词:

    // generateImagePrompt 生成专门用于图片生成的提示词(首帧静态画面)
    func (s *StoryboardService) generateImagePrompt(sb Storyboard) string {
        var parts []string
        
        // 1. 完整的场景背景描述
        if sb.Location != "" {
            locationDesc := sb.Location
            if sb.Time != "" {
                locationDesc += ", " + sb.Time
            }
            parts = append(parts, locationDesc)
        }
        
        // 2. 角色初始静态姿态(去除动作过程,只保留起始状态)
        if sb.Action != "" {
            initialPose := extractInitialPose(sb.Action)
            if initialPose != "" {
                parts = append(parts, initialPose)
            }
        }
        
        // 3. 情绪氛围
        // 4. 动漫风格
        parts = append(parts, "anime style, first frame")
        
        return strings.Join(parts, ", ")
    }

    // generateVideoPrompt 生成专门用于视频生成的提示词(包含运镜和动态元素)
    func (s *StoryboardService) generateVideoPrompt(sb Storyboard) string {
        var parts []string
        
        // 1. 人物动作
        // 2. 对话
        // 3. 镜头运动(视频特有)
        // 4. 镜头类型和角度
        // 5. 场景环境
        // 6. 环境氛围
        // 7. 情绪和结果
        // 8. 音频元素
        // 9. 视频风格要求
        parts = append(parts, "Style: cinematic anime style, smooth camera motion")
        
        return strings.Join(parts, ". ")
    }

    为什么要分开?因为图片生成和视频生成的需求不同:

    • 图片提示词:强调静态画面、构图、光影

    • 视频提示词:强调动态、运镜、时间流逝


    五、视频合成:FFmpeg的魔法时刻

    5.1 视频合成流程

    当所有分镜的视频都生成完成后,就到了最激动人心的时刻——把它们合成为一部完整的短剧。

    func (s *VideoMergeService) mergeVideoClips(client video.VideoClient, scenes []models.SceneClip) (*video.VideoResult, error) {
        // 1. 按顺序排列场景
        sort.Slice(scenes, func(i, j int) bool {
            return scenes[i].Order < scenes[j].Order
        })
        
        // 2. 准备FFmpeg合成选项
        clips := make([]ffmpeg.VideoClip, len(scenes))
        for i, scene := range scenes {
            clips[i] = ffmpeg.VideoClip{
                URL:        scene.VideoURL,
                Duration:   scene.Duration,
                StartTime:  scene.StartTime,
                EndTime:    scene.EndTime,
                Transition: scene.Transition,  // 转场效果
            }
        }
        
        // 3. 使用FFmpeg合成视频
        mergedPath, err := s.ffmpeg.MergeVideos(&ffmpeg.MergeOptions{
            OutputPath: outputPath,
            Clips:      clips,
        })
        
        return &video.VideoResult{
            VideoURL:  videoURL,
            Duration:  int(totalDuration),
            Completed: true,
        }, nil
    }

    5.2 转场效果实现

    系统支持丰富的转场效果,这是通过FFmpeg的xfade滤镜实现的:

    func (f *FFmpeg) mapTransitionType(transType string) string {
        switch strings.ToLower(transType) {
        // 淡入淡出类
        case "fade", "fadein", "fadeout":
            return "fade"
        case "fadeblack":
            return "fadeblack"
        case "fadewhite":
            return "fadewhite"
        
        // 滑动类
        case "slideleft":
            return "slideleft"
        case "slideright":
            return "slideright"
        case "slideup":
            return "slideup"
        case "slidedown":
            return "slidedown"
        
        // 擦除类
        case "wipeleft":
            return "wipeleft"
        case "wiperight":
            return "wiperight"
        
        // 圆形类
        case "circleopen":
            return "circleopen"
        case "circleclose":
            return "circleclose"
        
        // 其他特效
        case "dissolve":
            return "dissolve"
        case "pixelize":
            return "pixelize"
        
        default:
            return "fade"
        }
    }

    5.3 音频处理的智慧

    视频合成中最棘手的问题之一是音频处理。不同的视频片段可能有音频,也可能没有。系统的处理策略非常巧妙:

    func (f *FFmpeg) mergeWithXfade(inputPaths []string, clips []VideoClip, outputPath string) error {
        // 检测每个视频是否有音频流
        audioStreams := make([]bool, len(inputPaths))
        hasAnyAudio := false
        for i, path := range inputPaths {
            audioStreams[i] = f.hasAudioStream(path)
            if audioStreams[i] {
                hasAnyAudio = true
            }
        }
        
        // 为没有音频的视频生成静音轨道
        for i := 0; i < len(inputPaths); i++ {
            if !audioStreams[i] {
                // 生成静音轨道
                audioFilters = append(audioFilters,
                    fmt.Sprintf("anullsrc=channel_layout=stereo:sample_rate=44100:duration=%.2f[a%d]", 
                        totalDuration, i))
            } else if padDuration > 0 {
                // 有音频且需要延长:使用apad添加静音延长
                audioFilters = append(audioFilters,
                    fmt.Sprintf("[%d:a]apad=pad_dur=%.2f[a%d]", i, padDuration, i))
            }
        }
        
        // 音频交叉淡入淡出(避免转场时静音)
        for i := 0; i < len(inputPaths)-1; i++ {
            audioCrossfades = append(audioCrossfades,
                fmt.Sprintf("%sacrossfade=d=%.2f:c1=tri:c2=tri%s", 
                    inputLabel, transitionDuration, outputLabel))
        }
    }

    这段代码展示了几个关键技术点:

  • 静音轨道生成:对于没有音频的视频,自动生成静音轨道,保证音视频同步

  • 音频延长:为了配合视频转场,需要延长音频(freeze最后一帧的声音)

  • 交叉淡入淡出:转场时音频也要平滑过渡,避免突兀的静音

  • 5.4 分辨率统一

    不同AI服务生成的视频分辨率可能不同,合成前需要统一:

    // 检测视频分辨率,找到最大分辨率作为目标分辨率
    maxWidth := 0
    maxHeight := 0
    for i, path := range inputPaths {
        width, height := f.getVideoResolution(path)
        if width > maxWidth {
            maxWidth = width
        }
        if height > maxHeight {
            maxHeight = height
        }
    }

    // 为每个视频流添加缩放滤镜,统一分辨率
    for i := 0; i < len(inputPaths); i++ {
        scaleFilters = append(scaleFilters,
            fmt.Sprintf("[%d:v]scale=%d:%d:force_original_aspect_ratio=decrease,pad=%d:%d:(ow-iw)/2:(oh-ih)/2[v%d]",
                i, maxWidth, maxHeight, maxWidth, maxHeight, i))
    }

    这里使用了scale滤镜缩放视频,pad滤镜添加黑边保持长宽比,确保所有视频在合成后画面一致。


    六、前端架构:Vue3的现代化实践

    6.1 项目结构

    前端采用了标准的Vue3项目结构:

    web/src/
    ├── api/           # API接口封装
    ├── assets/        # 静态资源
    ├── components/    # 公共组件
    ├── locales/       # 国际化
    ├── router/        # 路由配置
    ├── stores/        # Pinia状态管理
    ├── types/         # TypeScript类型定义
    ├── utils/         # 工具函数
    └── views/         # 页面视图
        ├── dashboard/     # 仪表盘
        ├── drama/         # 剧本管理
        ├── editor/        # 编辑器
        ├── generation/    # 生成任务
        ├── script/        # 剧本编辑
        ├── settings/      # 设置
        ├── storyboard/    # 分镜管理
        └── workflow/      # 工作流

    6.2 API封装

    前端的API封装非常规范,每个模块都有独立的API文件:

    // web/src/api/drama.ts
    export const dramaApi = {
      // 获取剧本列表
      list: (params?: DramaListParams) => 
        request.get<DramaListResponse>('/api/v1/dramas', { params }),
      
      // 获取剧本详情
      get: (id: string) => 
        request.get<Drama>(`/api/v1/dramas/${id}`),
      
      // 创建剧本
      create: (data: CreateDramaRequest) => 
        request.post<Drama>('/api/v1/dramas', data),
      
      // 更新剧本
      update: (id: string, data: UpdateDramaRequest) => 
        request.put<Drama>(`/api/v1/dramas/${id}`, data),
      
      // 删除剧本
      delete: (id: string) => 
        request.delete(`/api/v1/dramas/${id}`),
    }

    6.3 国际化支持

    系统支持中英文切换,使用vue-i18n实现:

    // web/src/locales/index.ts
    import { createI18n } from 'vue-i18n'
    import zhCN from './zh-CN'
    import enUS from './en-US'

    const i18n = createI18n({
      legacy: false,
      locale: 'zh-CN',
      fallbackLocale: 'en-US',
      messages: {
        'zh-CN': zhCN,
        'en-US': enUS,
      },
    })

    export default i18n

    6.4 主题切换

    系统支持深色/浅色主题切换,在应用挂载前就应用主题,避免闪烁:

    // web/src/main.ts
    const savedTheme = localStorage.getItem('theme')
    if (savedTheme === 'dark' || 
        (!savedTheme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
      document.documentElement.classList.add('dark')
    }


    七、数据库设计:SQLite的高并发之道

    7.1 为什么选择SQLite?

    你可能会问:一个生产级应用为什么用SQLite而不是MySQL或PostgreSQL?

    答案是:简单就是美。

    对于这样一个工具型应用,SQLite有几个明显优势:

  • 零配置:不需要安装数据库服务器

  • 便携性:整个数据库就是一个文件,备份迁移超简单

  • 性能足够:对于单用户或小团队使用,SQLite的性能绑绑的

  • 7.2 WAL模式解决并发问题

    SQLite的一个常见问题是并发写入时的"database is locked"错误。系统通过启用WAL(Write-Ahead Logging)模式解决了这个问题:

    // infrastructure/database/database.go
    func NewDatabase(cfg config.DatabaseConfig) (*gorm.DB, error) {
        // 使用纯Go的SQLite驱动,支持CGO_ENABLED=0编译
        db, err := gorm.Open(sqlite.Open(cfg.Path), &gorm.Config{
            Logger: NewCustomLogger(logger.Default.LogMode(logger.Info)),
        })
        
        // 启用WAL模式,提高并发性能
        db.Exec("PRAGMA journal_mode=WAL")
        db.Exec("PRAGMA busy_timeout=5000")
        
        return db, nil
    }

    WAL模式的原理是:写操作先写入WAL文件,读操作可以同时进行,大大提高了并发性能。

    7.3 纯Go SQLite驱动

    项目使用了modernc.org/sqlite这个纯Go实现的SQLite驱动,而不是常见的mattn/go-sqlite3。

    为什么?因为mattn/go-sqlite3需要CGO,这意味着:

    • 编译时需要C编译器

    • 交叉编译很麻烦

    • Docker镜像需要包含C运行时

    而modernc.org/sqlite是纯Go实现,可以用CGO_ENABLED=0编译,生成的二进制文件可以在任何平台运行,Docker镜像也可以用最小的Alpine基础镜像。


    八、Docker部署:一键启动的优雅

    8.1 多阶段构建

    项目的Dockerfile采用了多阶段构建,最终镜像非常精简:

    # 阶段1: 构建前端
    FROM node:20-alpine AS frontend-builder
    WORKDIR /app/web
    COPY web/package*.json ./
    RUN npm install
    COPY web/ ./
    RUN npm run build

    # 阶段2: 构建后端
    FROM golang:1.23-alpine AS backend-builder
    ENV GOPROXY=https://goproxy.cn,direct
    WORKDIR /app
    COPY go.mod go.sum ./
    RUN go mod download
    COPY . .
    COPY –from=frontend-builder /app/web/dist ./web/dist
    # 纯Go编译,不需要CGO
    RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o huobao-drama .

    # 阶段3: 运行时镜像
    FROM alpine:latest
    RUN apk add –no-cache ca-certificates tzdata ffmpeg wget
    WORKDIR /app
    COPY –from=backend-builder /app/huobao-drama .
    COPY –from=frontend-builder /app/web/dist ./web/dist
    EXPOSE 5678
    CMD ["./huobao-drama"]

    这个Dockerfile的亮点:

  • 三阶段构建:前端构建、后端构建、运行时镜像分离

  • CGO_ENABLED=0:纯Go编译,不依赖C运行时

  • Alpine基础镜像:最终镜像只有几十MB

  • 内置FFmpeg:视频处理能力开箱即用

  • 8.2 Docker Compose编排

    services:
      huobao-drama:
        image: huobao-drama:latest
        container_name: huobao-drama
        ports:
          – "5678:5678"
        volumes:
          – huobao-data:/app/data
        environment:
          – TZ=Asia/Shanghai
        extra_hosts:
          – "host.docker.internal:host-gateway"  # 支持访问宿主机服务
        restart: unless-stopped
        healthcheck:
          test: ["CMD", "wget", "–spider", "http://localhost:5678/health"]
          interval: 30s
          timeout: 3s
          retries: 3

    volumes:
      huobao-data:
        driver: local

    特别值得一提的是host.docker.internal配置,这让容器可以访问宿主机上运行的服务(比如本地的Ollama),非常实用。


    九、实际应用场景

    9.1 短视频创作者

    对于抖音、快手等平台的短视频创作者来说,Huobao Drama可以:

    • 快速生成剧本创意

    • 自动设计角色形象

    • 批量生成分镜图片

    • 一键合成完整视频

    原本需要一个团队几天完成的工作,现在一个人几小时就能搞定。

    9.2 教育培训

    教育机构可以用它来:

    • 制作教学动画

    • 生成情景对话视频

    • 创建互动故事内容

    9.3 企业宣传

    企业可以用它来:

    • 制作产品介绍视频

    • 生成品牌故事短片

    • 创建内部培训材料

    9.4 个人创作

    对于有创意但缺乏技术能力的个人创作者:

    • 把脑海中的故事变成视频

    • 尝试不同的视觉风格

    • 快速验证创意可行性


    十、技术亮点总结

    回顾整个项目,有几个技术亮点值得特别强调:

    10.1 优雅的抽象设计

    无论是AI客户端、图片生成客户端还是视频生成客户端,都采用了接口抽象的设计。这种设计让系统具有极强的扩展性——添加新的AI服务提供商只需要实现相应的接口,不需要修改任何业务代码。

    10.2 异步任务处理

    AI生成任务的异步处理设计非常成熟:

    • 任务提交后立即返回

    • 后台goroutine持续轮询状态

    • 完成后自动更新数据库

    • 支持任务恢复(服务重启后自动恢复未完成的任务)

    10.3 FFmpeg深度集成

    视频合成部分对FFmpeg的使用非常专业:

    • 支持多种转场效果

    • 智能处理音频轨道

    • 自动统一视频分辨率

    • 支持视频裁剪和拼接

    10.4 国际化支持

    从后端的提示词模板到前端的UI文案,都支持中英文切换。这不仅是用户体验的提升,也为项目的国际化推广打下了基础。

    10.5 容器化部署

    多阶段Docker构建、纯Go编译、Alpine基础镜像,这些最佳实践让部署变得异常简单。一行docker-compose up -d就能启动整个服务。


    十一、未来展望

    11.1 更多AI模型支持

    随着AI技术的快速发展,未来可以集成更多的AI模型:

    • 更先进的视频生成模型(如Sora的后续版本)

    • 语音合成和配音

    • 背景音乐生成

    • 字幕自动生成

    11.2 协作功能

    目前系统主要面向单用户使用,未来可以添加:

    • 多用户协作

    • 项目分享

    • 版本控制

    • 评论和反馈

    11.3 更智能的AI导演

    当前的分镜生成主要依赖提示词工程,未来可以:

    • 训练专门的分镜生成模型

    • 学习用户的创作风格

    • 自动优化镜头节奏

    • 智能推荐转场效果

    11.4 移动端支持

    开发移动端应用,让创作者可以随时随地:

    • 查看生成进度

    • 预览生成结果

    • 简单的编辑调整

    • 一键分享到社交平台


    结语:AI时代的创作民主化

    Huobao Drama这个项目,让我看到了AI时代创作民主化的可能性。

    曾几何时,拍一部短剧是一件门槛很高的事情——你需要专业的设备、专业的团队、充足的预算。但现在,借助AI的力量,一个人、一台电脑、几个API Key,就能完成从创意到成片的全流程。

    这不是要取代专业的影视制作,而是为更多人打开了创作的大门。就像智能手机的普及让每个人都能成为摄影师一样,AI工具的普及也会让每个人都能成为"导演"。

    当然,工具只是工具,真正重要的还是创意本身。AI可以帮你生成画面,但故事的灵魂还是要靠人来赋予。

    最后,如果你对这个项目感兴趣,欢迎去GitHub上star一下,也欢迎提交PR贡献代码。开源的力量,就在于每一个人的参与。


    项目地址:https://github.com/chatfire-AI/huobao-drama

    更多AIGC文章

    RAG技术全解:从原理到实战的简明指南

    更多VibeCoding文章

    赞(0)
    未经允许不得转载:171主机测评 » 当AI学会拍短剧:Huobao Drama全栈AI短剧生成平台深度解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址