merge.go (view raw)
1package main
2
3import (
4 "fmt"
5 "io"
6 "net/http"
7 "os"
8 "os/exec"
9 "strconv"
10 "strings"
11)
12
13func saveUploadedFile(file io.Reader, path string) error {
14 out, err := os.Create(path)
15 if err != nil {
16 return err
17 }
18 defer out.Close()
19
20 _, err = io.Copy(out, file)
21 return err
22}
23
24func processHandler(w http.ResponseWriter, r *http.Request) {
25 if r.Method != http.MethodPost {
26 http.Error(w, "Metodo non supportato", http.StatusMethodNotAllowed)
27 return
28 }
29
30 // Parse del form per ottenere i file e gli altri parametri
31 if err := r.ParseMultipartForm(10 << 20); err != nil {
32 http.Error(w, "Errore parsing form", http.StatusBadRequest)
33 return
34 }
35
36 // Recupera i file audio e video
37 audioFile, _, err := r.FormFile("audioFile")
38 if err != nil {
39 http.Error(w, "File audio mancante", http.StatusBadRequest)
40 return
41 }
42 defer audioFile.Close()
43
44 videoFile, _, err := r.FormFile("videoFile")
45 if err != nil {
46 http.Error(w, "File video mancante", http.StatusBadRequest)
47 return
48 }
49 defer videoFile.Close()
50
51 // Salva i file temporaneamente
52 audioPath := "temp_audio.opus"
53 videoPath := "temp_video.mp4"
54 outputPath := "output.mp4"
55
56 if err := saveUploadedFile(audioFile, audioPath); err != nil {
57 http.Error(w, "Errore nel salvataggio del file audio", http.StatusInternalServerError)
58 return
59 }
60 if err := saveUploadedFile(videoFile, videoPath); err != nil {
61 http.Error(w, "Errore nel salvataggio del file video", http.StatusInternalServerError)
62 return
63 }
64
65 // Recupera i parametri di configurazione dal form
66 skip, _ := strconv.ParseFloat(r.FormValue("skip"), 64)
67 fade, _ := strconv.ParseFloat(r.FormValue("fade"), 64)
68 delay, _ := strconv.ParseFloat(r.FormValue("delay"), 64)
69 duration, _ := strconv.ParseFloat(r.FormValue("duration"), 64)
70
71 // Conversione delay in millisecondi
72 delayMs := int(delay * 1000)
73 fadeOutStart := duration - fade
74
75 // Costruzione del filtro audio
76 audioFilter := fmt.Sprintf("afade=t=out:st=%.2f:d=%.2f", fadeOutStart, fade)
77 if skip >= fade {
78 audioFilter = fmt.Sprintf("afade=t=in:ss=0:d=%.2f,%s", fade, audioFilter)
79 }
80
81 // Costruzione del comando ffmpeg
82 ffmpegCommand := []string{
83 "-i", videoPath,
84 "-i", audioPath,
85 "-filter_complex",
86 fmt.Sprintf("[1:a]atrim=start=%.2f:end=%.2f,asetpts=PTS-STARTPTS,%s,adelay=%d|%d[song];[0:a][song]amix=inputs=2:duration=first[audio_mix];[0:v]copy[v]",
87 skip, skip+duration, audioFilter, delayMs, delayMs),
88 "-map", "[v]", "-map", "[audio_mix]",
89 "-c:v", "libx264",
90 "-c:a", "aac",
91 "-shortest", outputPath,
92 "-y",
93 }
94
95 // Esecuzione del comando ffmpeg
96 cmd := exec.Command("ffmpeg", ffmpegCommand...)
97 println(strings.Join(ffmpegCommand, " "))
98 cmd.Stderr = os.Stderr // Mostra eventuali errori in console
99 cmd.Stdout = os.Stdout
100
101 if err := cmd.Run(); err != nil {
102 http.Error(w, "Errore durante l'esecuzione di ffmpeg", http.StatusInternalServerError)
103 return
104 }
105
106 // Impostazioni per scaricare il file di output
107 w.Header().Set("Content-Disposition", "attachment; filename=output.mp4")
108 w.Header().Set("Content-Type", "video/mp4")
109
110 // Restituisce il file output.mp4 in risposta
111 outputFile, err := os.Open(outputPath)
112 if err != nil {
113 http.Error(w, "Errore nell'apertura del file output", http.StatusInternalServerError)
114 return
115 }
116 defer outputFile.Close()
117
118 _, err = io.Copy(w, outputFile)
119 if err != nil {
120 http.Error(w, "Errore durante la copia del file", http.StatusInternalServerError)
121 return
122 }
123
124 // Pulizia dei file temporanei
125 os.Remove(audioPath)
126 os.Remove(videoPath)
127 os.Remove(outputPath)
128}