all repos — guesser @ d931017020f7620ab31581d5d4b62fb4d85a48c2

merge.go (view raw)

  1package main
  2
  3import (
  4	"errors"
  5	"fmt"
  6	"io"
  7	"net/http"
  8	"os"
  9	"os/exec"
 10	"strconv"
 11)
 12
 13const (
 14	outputPath = "output.mp4"
 15	audioPath  = "temp_audio.opus"
 16	videoPath  = "temp_video.mp4"
 17)
 18
 19var sem = make(chan struct{}, 1) // Semaforo con buffer 1
 20
 21func saveUploadedFile(file io.Reader, path string) error {
 22	out, err := os.Create(path)
 23	if err != nil {
 24		return err
 25	}
 26	defer out.Close()
 27
 28	_, err = io.Copy(out, file)
 29	return err
 30}
 31
 32// Funzione helper per restituire un errore di richiesta non valida
 33func badRequest(w http.ResponseWriter, reason string) error {
 34	http.Error(w, reason, http.StatusBadRequest)
 35	return errors.New(reason)
 36}
 37
 38// Funzione per leggere e validare i dati del form e salvare i file temporaneamente
 39func parseAndValidateForm(w http.ResponseWriter, r *http.Request) (skip, fade, delay, duration float64, err error) {
 40	if r.Method != http.MethodPost {
 41		err = badRequest(w, "Metodo non supportato")
 42		return
 43	}
 44
 45	// Parsing del form
 46	if err = r.ParseMultipartForm(10 << 20); err != nil {
 47		err = badRequest(w, "Errore nel form")
 48		return
 49	}
 50
 51	// Recupera i file audio e video
 52	audioFile, audioHeader, err := r.FormFile("audioFile")
 53	if err != nil {
 54		err = badRequest(w, "File audio mancante")
 55		return
 56	}
 57	defer audioFile.Close()
 58
 59	videoFile, videoHeader, err := r.FormFile("videoFile")
 60	if err != nil {
 61		err = badRequest(w, "File video mancante")
 62		return
 63	}
 64	defer videoFile.Close()
 65
 66	// Controllo delle dimensioni dei file
 67	const maxFileSize = 100 << 20 // 100 MB
 68	if audioHeader.Size > maxFileSize {
 69		err = badRequest(w, "File audio troppo grande")
 70		return
 71	}
 72	if videoHeader.Size > maxFileSize {
 73		err = badRequest(w, "File video troppo grande")
 74		return
 75	}
 76
 77	// Recupera e valida i parametri dal form
 78	if skip, err = strconv.ParseFloat(r.FormValue("skip"), 64); err != nil || skip < 0 {
 79		err = badRequest(w, "Parametro skip non valido")
 80		return
 81	}
 82
 83	if fade, err = strconv.ParseFloat(r.FormValue("fade"), 64); err != nil || fade < 0 {
 84		err = badRequest(w, "Parametro fade non valido")
 85		return
 86	}
 87
 88	if delay, err = strconv.ParseFloat(r.FormValue("delay"), 64); err != nil || delay < 0 {
 89		err = badRequest(w, "Parametro delay non valido")
 90		return
 91	}
 92
 93	if duration, err = strconv.ParseFloat(r.FormValue("duration"), 64); err != nil || duration <= 0 {
 94		err = badRequest(w, "Parametro duration non valido")
 95		return
 96	}
 97
 98	// Salva i file temporaneamente
 99	if err = saveUploadedFile(audioFile, audioPath); err != nil {
100		http.Error(w, "Errore nel salvataggio del file audio", http.StatusInternalServerError)
101		return
102	}
103	if err = saveUploadedFile(videoFile, videoPath); err != nil {
104		http.Error(w, "Errore nel salvataggio del file video", http.StatusInternalServerError)
105		return
106	}
107
108	return
109}
110
111// Funzione per gestire il processamento multimediale
112func processMedia(w http.ResponseWriter, skip, fade, delay, duration float64) error {
113	// Conversione delay in millisecondi
114	delayMs := int(delay * 1000)
115	fadeOutStart := duration - fade
116
117	// Costruzione del filtro audio
118	audioFilter := fmt.Sprintf("afade=t=out:st=%.2f:d=%.2f", fadeOutStart, fade)
119	if skip >= fade {
120		audioFilter = fmt.Sprintf("afade=t=in:ss=0:d=%.2f,%s", fade, audioFilter)
121	}
122
123	// Costruzione del comando ffmpeg
124	ffmpegCommand := []string{
125		"-i", videoPath,
126		"-i", audioPath,
127		"-filter_complex",
128		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]",
129			skip, skip+duration, audioFilter, delayMs, delayMs),
130		"-map", "[v]", "-map", "[audio_mix]",
131		"-c:v", "libx264",
132		"-c:a", "aac",
133		"-shortest", outputPath,
134		"-y",
135	}
136
137	// Esecuzione del comando ffmpeg
138	cmd := exec.Command("ffmpeg", ffmpegCommand...)
139	cmd.Stderr = os.Stderr
140	cmd.Stdout = os.Stdout
141
142	if err := cmd.Run(); err != nil {
143		return fmt.Errorf("errore durante l'esecuzione di ffmpeg: %v", err)
144	}
145
146	// Impostazioni per scaricare il file di output
147	w.Header().Set("Content-Disposition", "attachment; filename=output.mp4")
148	w.Header().Set("Content-Type", "video/mp4")
149
150	// Pulizia dei file temporanei
151	os.Remove(audioPath)
152	os.Remove(videoPath)
153
154	return nil
155}
156
157// Handler principale
158func processHandler(w http.ResponseWriter, r *http.Request) {
159	sem <- struct{}{}
160	defer func() { <-sem }() // Rilascia il semaforo alla fine
161
162	skip, fade, delay, duration, err := parseAndValidateForm(w, r)
163	if err != nil {
164		return // Gli errori vengono giĆ  gestiti in parseAndValidateForm
165	}
166
167	if err := processMedia(w, skip, fade, delay, duration); err != nil {
168		http.Error(w, "Errore durante il processamento multimediale", http.StatusInternalServerError)
169		return
170	}
171
172	// Restituisce il file output.mp4 in risposta
173	outputFile, err := os.Open(outputPath)
174	if err != nil {
175		return
176	}
177	defer outputFile.Close()
178
179	if _, err = io.Copy(w, outputFile); err != nil {
180		return
181	}
182
183	os.Remove(outputPath)
184}