代码初始化

This commit is contained in:
2026-05-19 14:33:06 +08:00
commit 219b7e39c7
18 changed files with 3311 additions and 0 deletions

View File

@@ -0,0 +1,156 @@
package video
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"time"
common "media/controller/common"
dto "media/model/dto/video"
service "media/service/video"
"gitea.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
type video struct{}
var Concat = new(video)
// ConcatVideosHandler 视频拼接
// 支持两种入参方式:
// 1. JSON body: {"video_urls":[...], "method":"auto"}
// 2. 文件上传: files 参数至少2个视频
func (c *video) ConcatVideosHandler(r *ghttp.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
// 优先尝试 JSON bodyURL 列表模式)
body := r.GetBody()
if len(body) > 0 && body[0] == '{' {
var req dto.ConcatReq
if json.Unmarshal(body, &req) == nil && len(req.VideoURLs) >= 2 {
if req.Method == "" {
req.Method = "auto"
}
tempDir := g.Cfg().MustGet(ctx, "ffmpeg.temp_dir", "resource/temp").String()
if !filepath.IsAbs(tempDir) {
absDir, _ := filepath.Abs(tempDir)
tempDir = absDir
}
os.MkdirAll(tempDir, 0755)
var savePaths []string
for _, videoURL := range req.VideoURLs {
savePath, dlErr := downloadFromURL(ctx, videoURL, tempDir)
if dlErr != nil {
continue
}
savePaths = append(savePaths, savePath)
}
if len(savePaths) < 2 {
cleanupConcat(savePaths)
r.Response.WriteJson(g.Map{"code": 400, "message": "成功下载的视频不足2个"})
return
}
svcRes, svcErr := service.Concat.Concat(ctx, &service.ConcatReq{
VideoPaths: savePaths,
Method: req.Method,
})
cleanupConcat(savePaths)
if svcErr != nil {
r.Response.WriteJson(g.Map{"code": 500, "message": "视频拼接失败: " + svcErr.Error()})
return
}
r.Response.WriteJson(g.Map{
"code": 200,
"message": "success",
"data": g.Map{
"outputPath": svcRes.OutputPath,
"fileSize": svcRes.FileSize,
"duration": svcRes.Duration,
"durationStr": svcRes.DurationStr,
"methodUsed": svcRes.MethodUsed,
"inputFiles": svcRes.InputFiles,
},
})
return
}
}
// 文件上传模式
savePaths, err := common.SaveUploadedFiles(r)
if err != nil || len(savePaths) < 2 {
r.Response.WriteJson(g.Map{"code": 400, "message": fmt.Sprintf("至少需要2个视频当前%d个", len(savePaths))})
return
}
svcRes, svcErr := service.Concat.Concat(ctx, &service.ConcatReq{
VideoPaths: savePaths,
Method: r.Get("method", "auto").String(),
})
service.CleanupConcat(savePaths)
if svcErr != nil {
r.Response.WriteJson(g.Map{"code": 500, "message": "视频拼接失败: " + svcErr.Error()})
return
}
r.Response.ServeFile(svcRes.OutputPath)
go func(path string) {
time.Sleep(5 * time.Second)
os.Remove(path)
}(svcRes.OutputPath)
}
func downloadFromURL(ctx context.Context, rawURL, tempDir string) (string, error) {
parsedURL, err := url.Parse(rawURL)
if err != nil {
return "", err
}
segments := strings.Split(parsedURL.Path, "/")
fileName := segments[len(segments)-1]
if fileName == "" {
fileName = fmt.Sprintf("video_%d.mp4", time.Now().UnixMilli())
}
savePath := filepath.Join(tempDir, fmt.Sprintf("%d_%s", time.Now().UnixMilli(), fileName))
client := &http.Client{Timeout: 10 * time.Minute}
resp, err := client.Get(rawURL)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("HTTP %d", resp.StatusCode)
}
out, err := os.Create(savePath)
if err != nil {
return "", err
}
defer out.Close()
_, err = io.Copy(out, resp.Body)
if err != nil {
os.Remove(savePath)
}
return savePath, err
}
func cleanupConcat(paths []string) {
for _, p := range paths {
os.Remove(p)
}
}