diff --git a/back/src/Kyoo.Core/Views/Helper/Transcoder.cs b/back/src/Kyoo.Core/Views/Helper/Transcoder.cs index 776dba98..d864425d 100644 --- a/back/src/Kyoo.Core/Views/Helper/Transcoder.cs +++ b/back/src/Kyoo.Core/Views/Helper/Transcoder.cs @@ -32,13 +32,14 @@ public abstract class TranscoderApi(IRepository repository, IThumbnailsMan : CrudThumbsApi(repository, thumbs) where T : class, IResource, IThumbnails, IQuery { - private Task _Proxy(string route, string path) + private Task _Proxy(string route, (string path, string route) info) { HttpProxyOptions proxyOptions = HttpProxyOptionsBuilder .Instance.WithBeforeSend( (ctx, req) => { - req.Headers.Add("X-Path", path); + req.Headers.Add("X-Path", info.path); + req.Headers.Add("X-Route", info.route); return Task.CompletedTask; } ) @@ -55,7 +56,7 @@ public abstract class TranscoderApi(IRepository repository, IThumbnailsMan return this.HttpProxyAsync($"http://transcoder:7666/{route}", proxyOptions); } - protected abstract Task GetPath(Identifier identifier); + protected abstract Task<(string path, string route)> GetPath(Identifier identifier); /// /// Direct stream @@ -117,10 +118,52 @@ public abstract class TranscoderApi(IRepository repository, IThumbnailsMan await _Proxy($"/audio/{audio}/{segment}", await GetPath(identifier)); } + /// + /// Get file info + /// + /// + /// Identify metadata about a file. + /// + /// The ID or slug of the . + /// The media infos of the file. + /// No episode with the given ID or slug could be found. [HttpGet("{identifier:id}/info")] - [PartialPermission(Kind.Play)] + [PartialPermission(Kind.Read)] public async Task GetInfo(Identifier identifier) { await _Proxy("/info", await GetPath(identifier)); } + + /// + /// Get thumbnail sprite + /// + /// + /// Get a sprite file containing all the thumbnails of the show. + /// + /// The ID or slug of the . + /// A sprite with an image for every X seconds of the video file. + /// No episode with the given ID or slug could be found. + [HttpGet("{identifier:id}/thumbnails.png")] + [PartialPermission(Kind.Read)] + public async Task GetThumbnails(Identifier identifier) + { + await _Proxy("/thumbnails.png", await GetPath(identifier)); + } + + /// + /// 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. + /// + /// The ID or slug of the . + /// A vtt file containing metadata about timing and x/y/width/height of the sprites of /thumbnails.png. + /// No episode with the given ID or slug could be found. + [HttpGet("{identifier:id}/thumbnails.vtt")] + [PartialPermission(Kind.Read)] + public async Task GetThumbnailsVtt(Identifier identifier) + { + await _Proxy("/thumbnails.vtt", await GetPath(identifier)); + } } diff --git a/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs b/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs index 8f96f203..af801914 100644 --- a/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/EpisodeApi.cs @@ -187,12 +187,13 @@ namespace Kyoo.Core.Api await libraryManager.WatchStatus.DeleteEpisodeStatus(id, User.GetIdOrThrow()); } - protected override async Task GetPath(Identifier identifier) + protected override async Task<(string path, string route)> GetPath(Identifier identifier) { - return await identifier.Match( + string path = await identifier.Match( async id => (await Repository.Get(id)).Path, async slug => (await Repository.Get(slug)).Path ); + return (path, $"/episodes/{identifier}"); } } } diff --git a/back/src/Kyoo.Core/Views/Resources/MovieApi.cs b/back/src/Kyoo.Core/Views/Resources/MovieApi.cs index 5c358029..b148f85d 100644 --- a/back/src/Kyoo.Core/Views/Resources/MovieApi.cs +++ b/back/src/Kyoo.Core/Views/Resources/MovieApi.cs @@ -233,12 +233,13 @@ namespace Kyoo.Core.Api await libraryManager.WatchStatus.DeleteMovieStatus(id, User.GetIdOrThrow()); } - protected override Task GetPath(Identifier identifier) + protected override async Task<(string path, string route)> GetPath(Identifier identifier) { - return identifier.Match( + string path = await identifier.Match( async id => (await Repository.Get(id)).Path, async slug => (await Repository.Get(slug)).Path ); + return (path, $"/movies/{identifier}"); } } } diff --git a/transcoder/main.go b/transcoder/main.go index 65f4f3ad..44c90973 100644 --- a/transcoder/main.go +++ b/transcoder/main.go @@ -182,10 +182,10 @@ func (h *Handler) GetInfo(c echo.Context) error { } // Run extractors to have them in cache h.extractor.RunExtractor(ret.Path, ret.Sha, &ret.Subtitles) - // go h.thumbnails.ExtractThumbnail( - // ret.Path, - // fmt.Sprintf("%s/%s/thumbnails.png", resource, slug), - // ) + go h.thumbnails.ExtractThumbnail( + ret.Path, + fmt.Sprintf("%s/thumbnails.png", c.Request().Header.Get("X-Route")), + ) return c.JSON(http.StatusOK, ret) } @@ -245,19 +245,16 @@ func (h *Handler) GetSubtitle(c echo.Context) error { // // Get a sprite file containing all the thumbnails of the show. // -// Path: /:resource/:slug/thumbnails.png +// Path: /thumbnails.png func (h *Handler) GetThumbnails(c echo.Context) error { - resource := c.Param("resource") - slug := c.Param("slug") - - path, err := GetPath(resource, slug) + path, err := GetPath(c) if err != nil { return err } out, err := h.thumbnails.ExtractThumbnail( path, - fmt.Sprintf("%s/%s/thumbnails.png", resource, slug), + fmt.Sprintf("%s/thumbnails.png", c.Request().Header.Get("X-Route")), ) if err != nil { return err @@ -273,17 +270,14 @@ func (h *Handler) GetThumbnails(c echo.Context) error { // // Path: /:resource/:slug/thumbnails.vtt func (h *Handler) GetThumbnailsVtt(c echo.Context) error { - resource := c.Param("resource") - slug := c.Param("slug") - - path, err := GetPath(resource, slug) + path, err := GetPath(c) if err != nil { return err } out, err := h.thumbnails.ExtractThumbnail( path, - fmt.Sprintf("%s/%s/thumbnails.png", resource, slug), + fmt.Sprintf("%s/thumbnails.png", c.Request().Header.Get("X-Route")), ) if err != nil { return err @@ -321,8 +315,8 @@ func main() { e.GET("/:quality/:chunk", h.GetVideoSegment) e.GET("/audio/:audio/:chunk", h.GetAudioSegment) e.GET("/info", h.GetInfo) - e.GET("/:resource/:slug/thumbnails.png", h.GetThumbnails) - e.GET("/:resource/:slug/thumbnails.vtt", h.GetThumbnailsVtt) + e.GET("/thumbnails.png", h.GetThumbnails) + e.GET("/thumbnails.vtt", h.GetThumbnailsVtt) e.GET("/:sha/attachment/:name", h.GetAttachment) e.GET("/:sha/subtitle/:name", h.GetSubtitle)