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 }