diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/compressed.mp4 b/compressed.mp4 new file mode 100644 index 0000000..e69de29 diff --git a/convert.go b/convert.go index 0b8a831..3839706 100644 --- a/convert.go +++ b/convert.go @@ -3,34 +3,66 @@ package main import ( "bytes" "context" - "fmt" "io" "log" "net/http" + "os" "os/exec" "time" ) func compressRAMHandler(w http.ResponseWriter, r *http.Request) { + start := time.Now() + log.Printf("[+] Новый запрос от %s", r.RemoteAddr) + if r.Method != http.MethodPost { http.Error(w, "Только POST метод", http.StatusMethodNotAllowed) return } + // 1. Создаем временный файл в RAM (/tmp работает на tmpfs) + tmpFile, err := os.CreateTemp("", "input-video-*.mp4") + if err != nil { + log.Printf("[-] Ошибка создания tmp-файла: %v", err) + http.Error(w, "Ошибка выpoint буфера RAM", http.StatusInternalServerError) + return + } + tmpFilePath := tmpFile.Name() + defer os.Remove(tmpFilePath) // Удаляем файл после завершения работы + + // 2. Копируем тело запроса во временный файл + copiedBytes, err := io.Copy(tmpFile, r.Body) + _ = tmpFile.Close() // Обязательно закрываем файл перед чтением в FFmpeg! + + if err != nil { + log.Printf("[-] Ошибка вычитывания Body: %v (получено %d байт)", err, copiedBytes) + http.Error(w, "Ошибка загрузки файла", http.StatusBadRequest) + return + } + + log.Printf("[+] Файл полностью загружен в RAM: %d байт (%.2f MB) за %v", + copiedBytes, float64(copiedBytes)/(1024*1024), time.Since(start)) + + if copiedBytes == 0 { + log.Printf("[-] Ошибка: Прилетело 0 байт!") + http.Error(w, "Пустой файл", http.StatusBadRequest) + return + } + + // 3. Заголовки ответа w.Header().Set("Content-Type", "video/mp4") w.Header().Set("Transfer-Encoding", "chunked") - ctx, cancel := context.WithTimeout(r.Context(), 60*time.Second) + ctx, cancel := context.WithTimeout(r.Context(), 120*time.Second) defer cancel() + // 4. Запуск FFmpeg args := []string{ "-y", - "-analyzeduration", "10M", - "-probesize", "10M", - "-i", "pipe:0", + "-i", tmpFilePath, "-c:v", "libx264", "-preset", "veryfast", - "-crf", "28", + "-crf", "26", "-threads", "4", "-movflags", "frag_keyframe+empty_moov", "-f", "mp4", @@ -39,27 +71,32 @@ func compressRAMHandler(w http.ResponseWriter, r *http.Request) { cmd := exec.CommandContext(ctx, "ffmpeg", args...) - cmd.Stdin = r.Body - - // Буфер для сбора ошибок FFmpeg var stderrBuf bytes.Buffer cmd.Stderr = &stderrBuf stdoutPipe, err := cmd.StdoutPipe() if err != nil { - http.Error(w, fmt.Sprintf("Ошибка pipe stdout: %v", err), http.StatusInternalServerError) + log.Printf("[-] Ошибка stdout pipe: %v", err) + http.Error(w, "Ошибка FFmpeg", http.StatusInternalServerError) return } if err := cmd.Start(); err != nil { - http.Error(w, fmt.Sprintf("Ошибка запуска FFmpeg: %v", err), http.StatusInternalServerError) + log.Printf("[-] Ошибка старта FFmpeg: %v", err) + http.Error(w, "Ошибка старта FFmpeg", http.StatusInternalServerError) return } - _, _ = io.Copy(w, stdoutPipe) + log.Printf("[+] FFmpeg успешно запущен, начинаем стримить ответ...") + + // 5. Потоково отдаем сжатый ответ + written, _ := io.Copy(w, stdoutPipe) if waitErr := cmd.Wait(); waitErr != nil { - log.Printf("FFmpeg завершился с ошибкой (%v):\n%s", waitErr, stderrBuf.String()) + log.Printf("[-] FFmpeg завершился с ошибкой: %v\nЛог FFmpeg:\n%s", waitErr, stderrBuf.String()) + } else { + log.Printf("[+] Обработка завершена успешно! Отправлено клиенту: %d байт (%.2f MB). Общее время: %v", + written, float64(written)/(1024*1024), time.Since(start)) } } diff --git a/go_build_convert b/go_build_convert index d843e38..6264bf0 100755 Binary files a/go_build_convert and b/go_build_convert differ diff --git a/main.zsh b/main.zsh new file mode 100644 index 0000000..04613be --- /dev/null +++ b/main.zsh @@ -0,0 +1,5 @@ +curl -X POST \ + -H "Content-Type: video/mp4" \ + --data-binary "@/home/hhu67/Downloads/fff" \ + https://api.convert.hhu67.pw/compress \ + -o compressed.mp4