diff --git a/back/src/Kyoo.Core/Views/Content/VideoApi.cs b/back/src/Kyoo.Core/Views/Content/VideoApi.cs index 7b67be57..bd8c0621 100644 --- a/back/src/Kyoo.Core/Views/Content/VideoApi.cs +++ b/back/src/Kyoo.Core/Views/Content/VideoApi.cs @@ -69,6 +69,16 @@ public class VideoApi : Controller await _Proxy($"{path}/direct"); } + [HttpGet("{path:base64}/direct/{identifier}")] + [PartialPermission(Kind.Play)] + [ProducesResponseType(StatusCodes.Status206PartialContent)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task GetDirectStream(string path, string identifier) + { + await _Proxy($"{path}/direct/{identifier}"); + } + + [HttpGet("{path:base64}/master.m3u8")] [PartialPermission(Kind.Play)] [ProducesResponseType(StatusCodes.Status206PartialContent)] diff --git a/transcoder/main.go b/transcoder/main.go index 68557a59..ae3aa715 100644 --- a/transcoder/main.go +++ b/transcoder/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "net/http" "strconv" @@ -194,6 +195,10 @@ func (h *Handler) GetInfo(c echo.Context) error { 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) } @@ -304,6 +309,7 @@ func main() { } e.GET("/:path/direct", DirectStream) + e.GET("/:path/direct/:identifier", DirectStream) e.GET("/:path/master.m3u8", h.GetMaster) e.GET("/:path/:video/:quality/index.m3u8", h.GetVideoIndex) e.GET("/:path/audio/:audio/index.m3u8", h.GetAudioIndex) diff --git a/transcoder/src/subtitles.go b/transcoder/src/subtitles.go new file mode 100644 index 00000000..f4c8816d --- /dev/null +++ b/transcoder/src/subtitles.go @@ -0,0 +1,65 @@ +package src + +import ( + "encoding/base64" + "fmt" + "path/filepath" + "regexp" + "strings" + + "golang.org/x/text/language" +) + +var separator = regexp.MustCompile(`[\s.]+`) + +func (mi *MediaInfo) SearchExternalSubtitles() error { + base_path := strings.TrimSuffix(mi.Path, filepath.Ext(mi.Path)) + matches, err := filepath.Glob(fmt.Sprintf("%s*", base_path)) + if err != nil { + return err + } + +outer: + for _, match := range matches { + for codec, ext := range SubtitleExtensions { + if strings.HasSuffix(match, ext) { + link := fmt.Sprintf( + "%s/direct/%s", + base64.RawURLEncoding.EncodeToString([]byte(match)), + filepath.Base(match), + ) + sub := Subtitle{ + Index: nil, + Codec: codec, + Extension: &ext, + IsExternal: true, + Path: &match, + Link: &link, + } + flags := separator.Split(match[len(base_path):], -1) + // remove extension from flags + flags = flags[:len(flags)-1] + + for _, flag := range flags { + switch strings.ToLower(flag) { + case "default": + sub.IsDefault = true + case "forced": + sub.IsForced = true + default: + lang, err := language.Parse(flag) + if err == nil && lang != language.Und { + lang := lang.String() + sub.Language = &lang + } else { + sub.Title = &flag + } + } + } + mi.Subtitles = append(mi.Subtitles, sub) + continue outer + } + } + } + return nil +}