mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-12-06 06:36:25 +00:00
Move metadata routes to it's own router
This commit is contained in:
@@ -3,11 +3,7 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
_ "github.com/zoriya/kyoo/transcoder/docs"
|
||||
|
||||
@@ -40,175 +36,6 @@ func ErrorHandler(err error, c echo.Context) {
|
||||
}{Errors: []string{message}})
|
||||
}
|
||||
|
||||
// Identify
|
||||
//
|
||||
// Identify metadata about a file.
|
||||
//
|
||||
// Path: /:path/info
|
||||
func (h *Handler) GetInfo(c echo.Context) error {
|
||||
path, sha, err := GetPath(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret, err := h.metadata.GetMetadata(c.Request().Context(), path, sha)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ret.SearchExternalSubtitles()
|
||||
if err != nil {
|
||||
fmt.Printf("Couldn't find external subtitles: %v", err)
|
||||
}
|
||||
return c.JSON(http.StatusOK, ret)
|
||||
}
|
||||
|
||||
// Get attachments
|
||||
//
|
||||
// Get a specific attachment.
|
||||
//
|
||||
// Path: /:path/attachment/:name
|
||||
func (h *Handler) GetAttachment(c echo.Context) (err error) {
|
||||
_, sha, err := GetPath(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name := c.Param("name")
|
||||
if err := SanitizePath(name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attachementStream, err := h.metadata.GetAttachment(c.Request().Context(), sha, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer utils.CleanupWithErr(&err, attachementStream.Close, "failed to close attachment reader")
|
||||
|
||||
mimeType, err := guessMimeType(name, attachementStream)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to guess mime type: %w", err)
|
||||
}
|
||||
|
||||
return c.Stream(200, mimeType, attachementStream)
|
||||
}
|
||||
|
||||
// Get subtitle
|
||||
//
|
||||
// Get a specific subtitle.
|
||||
//
|
||||
// Path: /:path/subtitle/:name
|
||||
func (h *Handler) GetSubtitle(c echo.Context) (err error) {
|
||||
_, sha, err := GetPath(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name := c.Param("name")
|
||||
if err := SanitizePath(name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
subtitleStream, err := h.metadata.GetSubtitle(c.Request().Context(), sha, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer utils.CleanupWithErr(&err, subtitleStream.Close, "failed to close subtitle reader")
|
||||
|
||||
mimeType, err := guessMimeType(name, subtitleStream)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to guess mime type: %w", err)
|
||||
}
|
||||
|
||||
// Default the mime type to text/plain if it is not recognized
|
||||
if mimeType == "" {
|
||||
mimeType = "text/plain"
|
||||
}
|
||||
|
||||
return c.Stream(200, mimeType, subtitleStream)
|
||||
}
|
||||
|
||||
// Get thumbnail sprite
|
||||
//
|
||||
// Get a sprite file containing all the thumbnails of the show.
|
||||
//
|
||||
// Path: /:path/thumbnails.png
|
||||
func (h *Handler) GetThumbnails(c echo.Context) (err error) {
|
||||
path, sha, err := GetPath(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sprite, err := h.metadata.GetThumbSprite(c.Request().Context(), path, sha)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer utils.CleanupWithErr(&err, sprite.Close, "failed to close thumbnail sprite reader")
|
||||
|
||||
return c.Stream(200, "image/png", sprite)
|
||||
}
|
||||
|
||||
// Get thumbnail vtt
|
||||
//
|
||||
// Get a vtt file containing timing/position of thumbnails inside the sprite file.
|
||||
// https://developer.bitmovin.com/playback/docs/webvtt-based-thumbnails for more info.
|
||||
//
|
||||
// Path: /:path/:resource/:slug/thumbnails.vtt
|
||||
func (h *Handler) GetThumbnailsVtt(c echo.Context) (err error) {
|
||||
path, sha, err := GetPath(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vtt, err := h.metadata.GetThumbVtt(c.Request().Context(), path, sha)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer utils.CleanupWithErr(&err, vtt.Close, "failed to close thumbnail vtt reader")
|
||||
|
||||
return c.Stream(200, "text/vtt", vtt)
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
transcoder *src.Transcoder
|
||||
metadata *src.MetadataService
|
||||
}
|
||||
|
||||
// Try to guess the mime type of a file based on its extension.
|
||||
// If the extension is not recognized, return an empty string.
|
||||
// If path is provided, it should contain a file extension (i.e. ".mp4").
|
||||
// If content is provided, it should be of type io.ReadSeeker. Instances of other types are ignored.
|
||||
// This implementation is based upon http.ServeContent.
|
||||
func guessMimeType(path string, content any) (string, error) {
|
||||
// This does not match a large number of different types that are likely in use.
|
||||
// TODO add telemetry to see what file extensions are used, then add logic
|
||||
// to detect the type based on the file extension.
|
||||
mimeType := ""
|
||||
|
||||
// First check the file extension, if there is one.
|
||||
ext := filepath.Ext(path)
|
||||
if ext != "" {
|
||||
if mimeType = mime.TypeByExtension(ext); mimeType != "" {
|
||||
return mimeType, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Try reading the first few bytes of the file to guess the mime type.
|
||||
// Only do this if seeking is supported
|
||||
if reader, ok := content.(io.ReadSeeker); ok {
|
||||
// 512 bytes is the most that DetectContentType will consider, so no
|
||||
// need to read more than that.
|
||||
var buf [512]byte
|
||||
n, _ := io.ReadFull(reader, buf[:])
|
||||
mimeType = http.DetectContentType(buf[:n])
|
||||
|
||||
// Reset the reader to the beginning of the file
|
||||
_, err := reader.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("mime type guesser failed to seek to beginning of file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return mimeType, nil
|
||||
}
|
||||
|
||||
// @title gocoder - Kyoo's transcoder
|
||||
// @version 1.0
|
||||
// @description Real time transcoder.
|
||||
@@ -248,11 +75,6 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
h := Handler{
|
||||
transcoder: transcoder,
|
||||
metadata: metadata,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
@@ -278,13 +100,9 @@ func main() {
|
||||
// return keys.LookupKeyID(kid.(string))
|
||||
},
|
||||
}))
|
||||
g.GET("/:path/info", h.GetInfo)
|
||||
g.GET("/:path/thumbnails.png", h.GetThumbnails)
|
||||
g.GET("/:path/thumbnails.vtt", h.GetThumbnailsVtt)
|
||||
g.GET("/:path/attachment/:name", h.GetAttachment)
|
||||
g.GET("/:path/subtitle/:name", h.GetSubtitle)
|
||||
|
||||
api.RegisterStreamHandlers(g)
|
||||
api.RegisterStreamHandlers(g, transcoder)
|
||||
api.RegisterMetadataHandlers(g, metadata)
|
||||
api.RegisterPProfHandlers(e)
|
||||
|
||||
e.Logger.Fatal(e.Start(":7666"))
|
||||
|
||||
Reference in New Issue
Block a user