admin 57eba68496
Some checks failed
continuous-integration/drone/push Build is failing
v.0.0.1 Создан сервис клипов
2025-09-02 11:58:10 +03:00

115 lines
2.7 KiB
Go

package ffmpeg
import (
"bytes"
"fmt"
"os/exec"
"strconv"
"strings"
)
type VideoProcessor interface {
GenerateThumbnail(videoData []byte) ([]byte, error)
GetDuration(videoData []byte) (int, error)
TrimVideo(videoData []byte, maxDuration int) ([]byte, error)
}
type videoProcessor struct {
}
func NewVideoProcessor() VideoProcessor {
return &videoProcessor{}
}
func (vp *videoProcessor) GenerateThumbnail(videoData []byte) ([]byte, error) {
cmd := exec.Command("ffmpeg",
"-i", "pipe:0", // читаем из stdin
"-ss", "00:00:01",
"-vframes", "1",
"-q:v", "2",
"-f", "image2pipe", // вывод в pipe
"-c:v", "mjpeg",
"pipe:1", // пишем в stdout
)
cmd.Stdin = bytes.NewReader(videoData)
var output bytes.Buffer
cmd.Stdout = &output
cmd.Stderr = &output // capture stderr for error messages
err := cmd.Run()
if err != nil {
return nil, fmt.Errorf("ffmpeg failed: %s, error: %w", output.String(), err)
}
if output.Len() == 0 {
return nil, fmt.Errorf("thumbnail generation produced empty output")
}
return output.Bytes(), nil
}
func (vp *videoProcessor) GetDuration(videoData []byte) (int, error) {
cmd := exec.Command("ffprobe",
"-i", "pipe:0",
"-show_entries", "format=duration",
"-v", "quiet",
"-of", "csv=p=0",
)
cmd.Stdin = bytes.NewReader(videoData)
output, err := cmd.Output()
if err != nil {
debugCmd := exec.Command("ffprobe", "-i", "pipe:0")
debugCmd.Stdin = bytes.NewReader(videoData)
debugOutput, _ := debugCmd.CombinedOutput()
return 0, fmt.Errorf("ffprobe failed: %w, debug output: %s", err, string(debugOutput))
}
durationStr := strings.TrimSpace(string(output))
duration, err := strconv.ParseFloat(durationStr, 64)
if err != nil {
return 0, fmt.Errorf("failed to parse duration '%s': %w", durationStr, err)
}
return int(duration), nil
}
func (vp *videoProcessor) TrimVideo(videoData []byte, maxDuration int) ([]byte, error) {
// Сначала получаем длительность
duration, err := vp.GetDuration(videoData)
if err != nil {
return nil, fmt.Errorf("failed to get video duration: %w", err)
}
// Если видео короче или равно лимиту, возвращаем оригинал
if duration <= maxDuration {
return videoData, nil
}
// Обрезаем видео
cmd := exec.Command("ffmpeg",
"-i", "pipe:0",
"-t", strconv.Itoa(maxDuration),
"-c", "copy",
"-f", "mp4",
"pipe:1",
)
cmd.Stdin = bytes.NewReader(videoData)
var output bytes.Buffer
cmd.Stdout = &output
cmd.Stderr = &output
err = cmd.Run()
if err != nil {
return nil, fmt.Errorf("ffmpeg trim failed: %s, error: %w", output.String(), err)
}
if output.Len() == 0 {
return nil, fmt.Errorf("trimmed video is empty")
}
return output.Bytes(), nil
}