diff --git a/transcoder/main.go b/transcoder/main.go index aeaa19f2..08c19c1a 100644 --- a/transcoder/main.go +++ b/transcoder/main.go @@ -54,6 +54,132 @@ func (h *Handler) GetMaster(c echo.Context) error { return c.String(http.StatusOK, ret) } +// Transcode video +// +// Transcode the video to the selected quality. +// This route can take a few seconds to respond since it will way for at least one segment to be +// available. +// +// Path: /:resource/:slug/:quality/index.m3u8 +func (h *Handler) GetVideoIndex(c echo.Context) error { + resource := c.Param("resource") + slug := c.Param("slug") + quality, err := src.QualityFromString(c.Param("quality")) + if err != nil { + return err + } + + client, err := GetClientId(c) + if err != nil { + return err + } + + path, err := GetPath(resource, slug) + if err != nil { + return err + } + + ret, err := h.transcoder.GetVideoIndex(path, quality, client) + if err != nil { + return err + } + return c.String(http.StatusOK, ret) +} + +// Transcode audio +// +// Get the selected audio +// This route can take a few seconds to respond since it will way for at least one segment to be +// available. +// +// Path: /:resource/:slug/audio/:audio/index.m3u8 +func (h *Handler) GetAudioIndex(c echo.Context) error { + resource := c.Param("resource") + slug := c.Param("slug") + audio := c.Param("audio") + + client, err := GetClientId(c) + if err != nil { + return err + } + + path, err := GetPath(resource, slug) + if err != nil { + return err + } + + ret, err := h.transcoder.GetAudioIndex(path, audio, client) + if err != nil { + return err + } + return c.String(http.StatusOK, ret) +} + +// Get transmuxed chunk +// +// Retrieve a chunk of a transmuxed video. +// +// Path: /:resource/:slug/:quality/segments-:chunk.ts +func (h *Handler) GetVideoSegment(c echo.Context) error { + resource := c.Param("resource") + slug := c.Param("slug") + quality, err := src.QualityFromString(c.Param("quality")) + if err != nil { + return err + } + segment, err := ParseSegment(c.Param("chunk")) + if err != nil { + return err + } + + client, err := GetClientId(c) + if err != nil { + return err + } + + path, err := GetPath(resource, slug) + if err != nil { + return err + } + + ret, err := h.transcoder.GetVideoSegment(path, quality, segment, client) + if err != nil { + return err + } + return c.File(ret) +} + +// Get audio chunk +// +// Retrieve a chunk of a transcoded audio. +// +// Path: /:resource/:slug/audio/:audio/segments-:chunk.ts +func (h *Handler) GetAudioSegment(c echo.Context) error { + resource := c.Param("resource") + slug := c.Param("slug") + audio := c.Param("audio") + segment, err := ParseSegment(c.Param("chunk")) + if err != nil { + return err + } + + client, err := GetClientId(c) + if err != nil { + return err + } + + path, err := GetPath(resource, slug) + if err != nil { + return err + } + + ret, err := h.transcoder.GetAudioSegment(path, audio, segment, client) + if err != nil { + return err + } + return c.File(ret) +} + // Identify // // # Identify metadata about a file @@ -88,6 +214,10 @@ func main() { e.GET("/:resource/:slug/direct", DirectStream) e.GET("/:resource/:slug/master.m3u8", h.GetMaster) + e.GET("/:resource/:slug/:quality/index.m3u8", h.GetVideoIndex) + e.GET("/:resource/:slug/audio/:audio/index.m3u8", h.GetAudioIndex) + e.GET("/:resource/:slug/:quality/:chunk", h.GetVideoSegment) + e.GET("/:resource/:slug/audio/:audio/:chunk", h.GetAudioSegment) e.GET("/:resource/:slug/info", GetInfo) e.Logger.Fatal(e.Start(":7666")) diff --git a/transcoder/src/quality.go b/transcoder/src/quality.go index 917cc091..60674abf 100644 --- a/transcoder/src/quality.go +++ b/transcoder/src/quality.go @@ -1,5 +1,11 @@ package src +import ( + "net/http" + + "github.com/labstack/echo/v4" +) + type Quality string const ( @@ -17,6 +23,20 @@ const ( // Purposfully removing Original from this list (since it require special treatments anyways) var Qualities = []Quality{P240, P360, P480, P720, P1080, P1440, P4k, P8k} +func QualityFromString(str string) (Quality, error) { + if str == string(Original) { + return Original, nil + } + + qualities := Qualities + for _, quality := range qualities { + if string(quality) == str { + return quality, nil + } + } + return Original, echo.NewHTTPError(http.StatusBadRequest, "Invalid quality") +} + // I'm not entierly sure about the values for bitrates. Double checking would be nice. func (v Quality) AverageBitrate() uint32 { switch v { diff --git a/transcoder/src/transcoder.go b/transcoder/src/transcoder.go index 9f392e9c..f6bc8304 100644 --- a/transcoder/src/transcoder.go +++ b/transcoder/src/transcoder.go @@ -73,3 +73,29 @@ func (t *Transcoder) cleanUnused() { delete(t.streams, path) } } + +func (t *Transcoder) GetVideoIndex(path string, quality Quality, client string) (string, error) { + return "", nil +} + +func (t *Transcoder) GetAudioIndex(path string, audio string, client string) (string, error) { + return "", nil +} + +func (t *Transcoder) GetVideoSegment( + path string, + quality Quality, + segment int32, + client string, +) (string, error) { + return "", nil +} + +func (t *Transcoder) GetAudioSegment( + path string, + audio string, + segment int32, + client string, +) (string, error) { + return "", nil +} diff --git a/transcoder/utils.go b/transcoder/utils.go index 911276c4..c1c6e1aa 100644 --- a/transcoder/utils.go +++ b/transcoder/utils.go @@ -64,15 +64,26 @@ func GetClientId(c echo.Context) (string, error) { return key, nil } +func ParseSegment(segment string) (int32, error) { + var ret int32 + _, err := fmt.Sscanf(segment, "segment-%d.ts", &ret) + if err != nil { + return 0, echo.NewHTTPError(http.StatusBadRequest, "Could not parse segment.") + } + return ret, nil +} + func ErrorHandler(err error, c echo.Context) { code := http.StatusInternalServerError + var message string if he, ok := err.(*echo.HTTPError); ok { code = he.Code + message = fmt.Sprint(he.Message) } else { c.Logger().Error(err) + message = "Internal server error" } c.JSON(code, struct { Errors []string `json:"errors"` - }{Errors: []string{fmt.Sprint(err)}}) + }{Errors: []string{message}}) } -