package main import ( "compress/bzip2" "compress/flate" "compress/gzip" "errors" "fmt" "git.gammaspectra.live/S.O.N.G/Ignite/frame" "github.com/ulikunitz/xz" "io" "log" "maps" "net/http" "os" "time" ) func handleDecompress(contentEncoding string, reader io.ReadCloser) (io.ReadCloser, error) { var err error switch contentEncoding { case "gzip": reader, err = gzip.NewReader(reader) if err != nil { return nil, err } case "bzip2": reader = io.NopCloser(bzip2.NewReader(reader)) case "deflate": reader = flate.NewReader(reader) case "xz": r, err := xz.NewReader(reader) if err != nil { return nil, err } reader = io.NopCloser(r) case "": return reader, nil default: return nil, errors.New("unsupported encoding") } return reader, nil } func encodeFromReader(reader io.ReadCloser, job *Job, inputMimeType string, w http.ResponseWriter) { defer reader.Close() d := GetDecoderByMimeType(inputMimeType) if d == nil { //try for generic reader d = GetDecoderByMimeType("*") if d == nil { w.WriteHeader(http.StatusBadRequest) return } } defer func() { log.Printf("[job %s] finished, took %s", job.Id, time.Now().Sub(time.Unix(int64(job.Status.Start.Load()), 0))) }() job.Status.Start.Store(uint64(time.Now().Unix())) job.Status.Read.Store(0) job.Status.Processed.Store(0) settings := make(map[string]any) maps.Copy(settings, job.Config.Decoder.Settings) if len(job.Config.Timecodes) > 0 { settings["timecodes"] = job.Config.Timecodes } decoder, err := d.New(reader, settings) if err != nil { job.Logger.Printf("[error]: %s", err.Error()) w.Header().Set("x-encoder-error", "") w.Header().Set("x-decoder-error", err.Error()) w.WriteHeader(http.StatusBadRequest) return } //check decoder frame properties match decProps := decoder.Properties() if decProps.Width != job.Config.Properties.Width || decProps.Height != job.Config.Properties.Height || decProps.ColorSpace != job.Config.Properties.ColorSpace || decProps.FullColorRange != job.Config.Properties.FullColorRange || decProps.TimeBase() != job.Config.Properties.TimeBase() { w.Header().Set("x-encoder-error", "") w.Header().Set("x-decoder-error", "mismatched config properties") w.WriteHeader(http.StatusBadRequest) return } mimeType := "application/octet-stream" if e := GetEncoder(job.Config.Encoder.Name); e != nil { mimeType = e.MimeType } w.Header().Set("Content-Type", mimeType) w.Header().Add("Trailer", "x-encoder-error, x-decoder-error") w.WriteHeader(http.StatusOK) job.Logger = log.New(os.Stderr, fmt.Sprintf("[job %s] ", job.Id), log.LstdFlags) err = job.Init(w) if err != nil { job.Logger.Printf("[error]: %s", err.Error()) w.Header().Set("x-encoder-error", err.Error()) w.Header().Set("x-decoder-error", "") return } defer job.Close() var f frame.Frame for { f, err = decoder.Decode() if err != nil { if errors.Is(err, io.EOF) { //we are done break } job.Logger.Printf("[error]: %s", err.Error()) w.Header().Set("x-encoder-error", "") w.Header().Set("x-decoder-error", err.Error()) return } job.Status.Read.Add(1) err = job.Encoder.Encode(f) if err != nil { job.Logger.Printf("[error]: %s", err.Error()) w.Header().Set("x-encoder-error", err.Error()) w.Header().Set("x-decoder-error", "") return } f.Return() job.Status.Processed.Add(1) //log.Printf("[job %s] %d", job.Id, job.Status.Read.Load()) } err = job.Encoder.Flush() if err != nil { job.Logger.Printf("[error]: %s", err.Error()) w.Header().Set("x-encoder-error", err.Error()) w.Header().Set("x-decoder-error", "") return } }