2025-09-02 23:17:24 +03:00

131 lines
3.4 KiB
Go

package ffmpeg
import (
"bytes"
"fmt"
"os"
"os/exec"
)
type VideoProcessor interface {
GenerateThumbnail(videoData []byte) ([]byte, error)
TrimVideo(videoData []byte, maxDuration int) ([]byte, error)
}
type videoProcessor struct {
tempDir string // опционально: каталог для временных файлов
}
func NewVideoProcessor() VideoProcessor {
return &videoProcessor{
tempDir: os.TempDir(), // используем системный temp каталог
}
}
func (vp *videoProcessor) withTempFile(pattern string, data []byte, fn func(filename string) error) error {
// Создаем временный файл
file, err := os.CreateTemp(vp.tempDir, pattern)
if err != nil {
return fmt.Errorf("failed to create temp file: %w", err)
}
tempPath := file.Name()
// Гарантируем удаление файла
defer func() {
if _, err := os.Stat(tempPath); err == nil {
os.Remove(tempPath)
}
}()
// Записываем данные
if _, err := file.Write(data); err != nil {
return fmt.Errorf("failed to write to temp file: %w", err)
}
file.Close()
// Вызываем функцию обработки
return fn(tempPath)
}
func (vp *videoProcessor) GenerateThumbnail(videoData []byte) ([]byte, error) {
var thumbnailData []byte
err := vp.withTempFile("thumbnail_*.mp4", videoData, func(inputPath string) error {
cmd := exec.Command("ffmpeg",
"-i", inputPath,
"-ss", "00:00:01",
"-vframes", "1",
"-q:v", "2",
"-f", "image2pipe",
"-c:v", "mjpeg",
"pipe:1",
)
var output bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &output
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("ffmpeg failed: %s, error: %w", stderr.String(), err)
}
thumbnailData = output.Bytes()
return nil
})
return thumbnailData, err
}
func (vp *videoProcessor) TrimVideo(videoData []byte, maxDuration int) ([]byte, error) {
// 1. Создаем временный файл для входного видео
inputFile, err := os.CreateTemp(vp.tempDir, "trim_input_*.mp4")
if err != nil {
return nil, fmt.Errorf("failed to create temp file: %w", err)
}
defer os.Remove(inputFile.Name())
defer inputFile.Close()
// 2. Записываем видео данные во временный файл
if _, err := inputFile.Write(videoData); err != nil {
return nil, fmt.Errorf("failed to write to temp file: %w", err)
}
inputFile.Close()
// 3. Создаем временный файл для выходного видео
outputFile, err := os.CreateTemp(vp.tempDir, "trim_output_*.mp4")
if err != nil {
return nil, fmt.Errorf("failed to create output temp file: %w", err)
}
defer os.Remove(outputFile.Name())
defer outputFile.Close()
// 4. Выполняем обрезку
cmd := exec.Command("ffmpeg",
"-i", inputFile.Name(),
"-t", fmt.Sprintf("%d", maxDuration),
"-c", "copy",
"-y", // overwrite output file
outputFile.Name(),
)
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return nil, fmt.Errorf("ffmpeg trim failed: %s, error: %w", stderr.String(), err)
}
// 5. Читаем обрезанное видео
trimmedData, err := os.ReadFile(outputFile.Name())
if err != nil {
return nil, fmt.Errorf("failed to read trimmed video: %w", err)
}
if len(trimmedData) == 0 {
return nil, fmt.Errorf("trimmed video is empty")
}
return trimmedData, nil
}