105 lines
2.6 KiB
Go
105 lines
2.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"aroll/store"
|
|
"aroll/transcode"
|
|
"aroll/ws"
|
|
)
|
|
|
|
type exportRequest struct {
|
|
Filename string `json:"filename"`
|
|
Segments []transcode.Segment `json:"segments"`
|
|
}
|
|
|
|
// ExportHandler fetches the input video from Redis, runs FFmpeg to cut the
|
|
// silence, stores the output back in Redis, then cleans up the temp files.
|
|
// The output key is sent to the frontend via a "done" WebSocket message.
|
|
func ExportHandler(st *store.Store, hub *ws.Hub) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost {
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
var req exportRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "bad JSON: "+err.Error(), http.StatusBadRequest)
|
|
return
|
|
}
|
|
if len(req.Segments) == 0 {
|
|
http.Error(w, "no segments", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
data, err := st.Get(context.Background(), req.Filename)
|
|
if err != nil {
|
|
http.Error(w, "file not found in store (may have expired)", http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
ext := filepath.Ext(req.Filename)
|
|
|
|
go func() {
|
|
// Write input bytes to a temp file for FFmpeg
|
|
inTmp, err := os.CreateTemp("", "aroll-in-*"+ext)
|
|
if err != nil {
|
|
broadcastError(hub, "create input temp: "+err.Error())
|
|
return
|
|
}
|
|
defer os.Remove(inTmp.Name())
|
|
|
|
if _, err := inTmp.Write(data); err != nil {
|
|
inTmp.Close()
|
|
broadcastError(hub, "write input temp: "+err.Error())
|
|
return
|
|
}
|
|
inTmp.Close()
|
|
|
|
// Create an output temp file for FFmpeg to write into
|
|
outTmp, err := os.CreateTemp("", "aroll-out-*.mp4")
|
|
if err != nil {
|
|
broadcastError(hub, "create output temp: "+err.Error())
|
|
return
|
|
}
|
|
outTmp.Close()
|
|
defer os.Remove(outTmp.Name())
|
|
|
|
// Run FFmpeg — progress streamed via WebSocket
|
|
if err := transcode.ExportSegments(
|
|
inTmp.Name(), outTmp.Name(), req.Segments, hub.Broadcast,
|
|
); err != nil {
|
|
broadcastError(hub, err.Error())
|
|
return
|
|
}
|
|
|
|
// Read the output bytes and store them in Redis
|
|
outData, err := os.ReadFile(outTmp.Name())
|
|
if err != nil {
|
|
broadcastError(hub, "read output: "+err.Error())
|
|
return
|
|
}
|
|
|
|
outputKey := "output-" + store.NewID()
|
|
if err := st.Set(context.Background(), outputKey, outData); err != nil {
|
|
broadcastError(hub, "store output: "+err.Error())
|
|
return
|
|
}
|
|
|
|
// Tell the frontend where to download from
|
|
msg, _ := json.Marshal(map[string]string{
|
|
"type": "done",
|
|
"message": outputKey,
|
|
})
|
|
hub.Broadcast(msg)
|
|
}()
|
|
|
|
w.WriteHeader(http.StatusAccepted)
|
|
}
|
|
}
|