初始化
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
// 本文件负责按创作项目类型筛选当前剧集可下载的视频,并生成 ZIP 文件清单。
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"juhe-factory/api/internal/videoarchive"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// episodeVideoArchiveRow 描述生成视频查询结果及其在分镜中的业务状态。
|
||||
type episodeVideoArchiveRow struct {
|
||||
ObjectKey string
|
||||
MimeType string
|
||||
SequenceNo int
|
||||
Active bool
|
||||
}
|
||||
|
||||
// EpisodeVideoArchive 查询当前剧集视频:短剧创作仅包含主视频和备选视频,视频转绘包含主视频和历史视频。
|
||||
func (s *Creative) EpisodeVideoArchive(userID, projectID, episodeID uuid.UUID) (videoarchive.Manifest, error) {
|
||||
// 校验项目归属并获取项目类型,当前剧集视频 ZIP 仅对短剧创作和视频转绘开放。
|
||||
var project struct {
|
||||
ProjectType string
|
||||
}
|
||||
if err := s.DB.Table("creative_projects").Select("project_type").
|
||||
Where("id=? AND user_id=? AND deleted_at IS NULL", projectID, userID).
|
||||
Take(&project).Error; err != nil {
|
||||
return videoarchive.Manifest{}, err
|
||||
}
|
||||
if project.ProjectType != "premium_drama" && project.ProjectType != "video_redraw" {
|
||||
return videoarchive.Manifest{}, gorm.ErrRecordNotFound
|
||||
}
|
||||
var episode struct {
|
||||
ProjectName string
|
||||
EpisodeName string
|
||||
}
|
||||
if err := s.DB.Table("project_episodes episode").
|
||||
Select("project.name AS project_name,episode.name AS episode_name").
|
||||
Joins("JOIN creative_projects project ON project.id=episode.project_id AND project.deleted_at IS NULL").
|
||||
Where("episode.id=? AND episode.project_id=? AND episode.deleted_at IS NULL", episodeID, projectID).
|
||||
Take(&episode).Error; err != nil {
|
||||
return videoarchive.Manifest{}, err
|
||||
}
|
||||
|
||||
rows := make([]episodeVideoArchiveRow, 0)
|
||||
query := s.DB.Table("generation_outputs output").
|
||||
Select(`media.object_key,media.mime_type,storyboard.sequence_no,
|
||||
storyboard.active_output_id=output.id AS active`).
|
||||
Joins("JOIN generation_tasks task ON task.id=output.task_id AND task.task_type='video_generation'").
|
||||
Joins("JOIN episode_storyboards storyboard ON storyboard.id=task.storyboard_id AND storyboard.deleted_at IS NULL").
|
||||
Joins("JOIN media_assets media ON media.id=output.media_asset_id AND media.deleted_at IS NULL").
|
||||
Where("storyboard.episode_id=?", episodeID)
|
||||
if project.ProjectType == "premium_drama" {
|
||||
query = query.Where("storyboard.active_output_id=output.id OR coalesce(output.metadata->>'candidate','false')='true'")
|
||||
}
|
||||
if err := query.Order("storyboard.sequence_no ASC,(storyboard.active_output_id=output.id) DESC,output.created_at DESC").Find(&rows).Error; err != nil {
|
||||
return videoarchive.Manifest{}, err
|
||||
}
|
||||
if len(rows) == 0 {
|
||||
return videoarchive.Manifest{}, errors.New("当前剧集暂无可下载视频")
|
||||
}
|
||||
|
||||
entries := make([]videoarchive.Entry, 0, len(rows))
|
||||
versions := make(map[int]int)
|
||||
for _, row := range rows {
|
||||
label := "主视频"
|
||||
if !row.Active {
|
||||
versions[row.SequenceNo]++
|
||||
if project.ProjectType == "premium_drama" {
|
||||
label = fmt.Sprintf("备选视频%d", versions[row.SequenceNo])
|
||||
} else {
|
||||
label = fmt.Sprintf("历史视频%d", versions[row.SequenceNo])
|
||||
}
|
||||
}
|
||||
name := fmt.Sprintf("分镜%03d-%s%s", row.SequenceNo, label, videoArchiveExtension(row.ObjectKey, row.MimeType))
|
||||
entries = append(entries, videoarchive.Entry{ObjectKey: row.ObjectKey, Name: videoarchive.SafeFilename(name, "视频.mp4")})
|
||||
}
|
||||
archiveName := videoarchive.SafeFilename(episode.ProjectName+episode.EpisodeName, "剧集视频") + ".zip"
|
||||
return videoarchive.Manifest{Name: archiveName, Entries: entries}, nil
|
||||
}
|
||||
|
||||
// videoArchiveExtension 根据已保存的媒体类型和对象键生成稳定的视频扩展名。
|
||||
func videoArchiveExtension(objectKey, mimeType string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(strings.Split(mimeType, ";")[0])) {
|
||||
case "video/webm":
|
||||
return ".webm"
|
||||
case "video/quicktime":
|
||||
return ".mov"
|
||||
case "video/x-m4v":
|
||||
return ".m4v"
|
||||
case "video/x-matroska":
|
||||
return ".mkv"
|
||||
case "video/x-msvideo":
|
||||
return ".avi"
|
||||
case "video/mp4":
|
||||
return ".mp4"
|
||||
}
|
||||
extension := strings.ToLower(path.Ext(objectKey))
|
||||
if map[string]bool{".mp4": true, ".webm": true, ".mov": true, ".m4v": true, ".mkv": true, ".avi": true}[extension] {
|
||||
return extension
|
||||
}
|
||||
return ".mp4"
|
||||
}
|
||||
Reference in New Issue
Block a user